C/C++ 变量详解
Posted 余识-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++ 变量详解相关的知识,希望对你有一定的参考价值。
文章目录
前言
对C++学习感兴趣的可以看看这篇文章哦:C/C++教程
一、静态变量与动态变量
1. 概念
在程序设计中,变量是程序中最基本的元素之一。变量可以用于存储不同类型的数据,包括数字、字符串、布尔值等。静态变量和动态变量是两种不同类型的变量,它们在程序中的使用方式和作用有所不同。
静态变量是指在程序运行时分配内存空间,并且在整个程序的运行过程中保持不变的变量。在C++中,静态变量可以在函数内部或者全局变量中声明。例如:
void test()
static int count = 0;
count++;
std::cout << "count: " << count << std::endl;
int main()
for(int i=0; i<5; i++)
test();
return 0;
在上面的代码中,变量count是一个静态变量,它在函数test()内部被声明,并且被赋初值为0。每当test()函数被调用时,count的值会被增加1,并且在整个程序运行过程中保持不变。在程序运行过程中,会输出5个值,分别为1、2、3、4、5。
动态变量是指在程序运行时动态分配内存空间的变量。在C++中,可以使用关键字new来动态分配内存。例如:
int main()
int *ptr = new int;
*ptr = 10;
std::cout << "ptr: " << *ptr << std::endl;
delete ptr;
return 0;
在上面的代码中,变量ptr是一个动态变量,它使用关键字new来动态分配内存,并且赋初值为10。在程序运行过程中,会输出ptr的值为10。在变量ptr不再被使用时,需要使用delete关键字释放内存空间。
2. 区别
静态变量和动态变量之间有几个显著的区别:
- 内存分配方式:静态变量在程序运行时分配内存空间,而动态变量是在程序运行时动态分配内存空间。
- 生命周期:静态变量在整个程序运行过程中保持不变,而动态变量的生命周期取决于程序运行时的内存管理。
- 可见性:静态变量的作用域可以是全局的或者函数内部的,而动态变量通常是局部变量。
- 变量类型:静态变量可以是常量或非常量,而动态变量只能是非常量。
3. 使用方法和注意事项
在使用静态变量和动态变量时,需要注意以下几点:
3.1 静态变量
- 静态变量可以在函数内部或者全局变量中声明。在函数内部声明的静态变量只能在该函数内部访问,而全局静态变量可以在整个程序中访问。
- 静态变量的初始值只会被赋值一次,在程序运行过程中保持不变。在函数内部声明的静态变量只会在第一次调用该函数时被初始化,在后续调用中保持不变。
- 静态变量的作用域可以是全局的或者函数内部的,全局静态变量只能在该文件中访问。在不同的文件中定义相同名称的静态变量是不会冲突的。
3.2 动态变量
- 动态变量需要使用new关键字来动态分配内存空间,并且需要使用delete关键字来释放内存空间。如果没有释放内存空间,就会导致内存泄漏。
- 动态变量的生命周期取决于程序运行时的内存管理。在程序运行过程中,需要确保释放不再使用的动态变量的内存空间。
- 动态变量通常是局部变量,只能在函数内部访问。如果需要在函数外部访问动态变量,可以使用返回指针的方式。
4. 结论
静态变量和动态变量是程序设计中常用的两种变量类型,它们在内存分配方式、生命周期、可见性和变量类型等方面存在明显的区别。在使用静态变量和动态变量时,需要根据实际情况选择合适的变量类型,并且遵循使用方法和注意事项,以确保程序的正确性和稳定性。
二、全局变量与局部变量
1. 区别
全局变量和局部变量是C++中两种不同类型的变量,它们之间的主要区别如下:
-
作用域不同:全局变量的作用域是整个程序,而局部变量的作用域只在定义它的函数或代码块内。
-
生命周期不同:全局变量的生命周期是整个程序运行期间,而局部变量的生命周期只在它所在的函数或代码块执行期间。
-
内存位置不同:全局变量存储在静态存储区,局部变量存储在栈上。
2. 全局变量的使用方法和注意事项
全局变量可以在程序中的任何地方使用,但在使用前需要注意以下几点:
- 声明和定义:需要在文件的顶部声明全局变量,并在函数外部定义它们。
// 声明全局变量
extern int global_variable;
// 定义全局变量
int global_variable = 0;
-
命名冲突:全局变量可能会和其他文件或库中的变量发生命名冲突,需要避免重复命名。
-
可见性:全局变量的作用域是整个程序,因此可能会被程序中的任何部分访问或修改,需要注意变量的可见性和安全性。
-
初始化:全局变量默认情况下会被初始化为0或空指针,也可以在定义时进行初始化。
// 定义并初始化全局变量
int global_variable = 10;
3. 局部变量的使用方法和注意事项
局部变量只在定义它的函数或代码块内可见,使用时需要注意以下几点:
- 声明和定义:局部变量需要在函数或代码块内部声明和定义。
void some_function()
// 声明和定义局部变量
int local_variable = 0;
-
生命周期:局部变量的生命周期仅限于定义它的函数或代码块的执行期间,执行结束后会被自动销毁。
-
内存位置:局部变量存储在栈上,因此在定义时需要注意变量的大小和栈的大小。
void some_function()
// 定义一个较大的局部变量
char buffer[1024];
// ...
- 初始化:局部变量可以在定义时进行初始化,也可以在后续的代码中进行初始化。
void some_function()
// 定义并初始化局部变量
int local_variable = 10;
// ...
// 后续代码中进行初始化
local_variable = 20;
4. 总结
全局变量和局部变量在C++中有着不同的作用和使用方式,需要注意它们的区别和注意事项,避免出现不必要的错误。全局变量适用于需要在程序中的多个位置使用的变量,而局部变量适用于只在函数或代码块内部使用的变量。在使用变量时,需要注意变量的作用域、生命周期、内存位置、命名冲突和初始化等问题,以确保程序的正确性和稳定性。
C++ 条件变量使用详解
(给CPP开发者加星标,提升C/C++技能)
来源:Jimmy1224 https://blog.csdn.net/c_base_jin/article/details/89741247
【导读】:本文主要讲解条件变量的详细使用方法。
condition_variable介绍
在C++11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。
其主要成员函数如下:
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:
-
一个线程因等待"条件变量的条件成立"而挂起; -
另外一个线程使"条件成立",给出信号,从而唤醒被等待的线程。
为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起;通常情况下这个锁是std::mutex,并且管理这个锁 只能是 std::unique_lockstd::mutex RAII模板类。
上面提到的两个步骤,分别是使用以下两个方法实现:
-
等待条件成立使用的是condition_variable类成员wait 、wait_for 或 wait_until。
-
给出信号使用的是condition_variable类成员notify_one或者notify_all函数。
细节说明
在条件变量中只能使用std::unique_lock< std::mutex >说明
unique_lock和lock_guard都是管理锁的辅助类工具,都是RAII风格;它们是在定义时获得锁,在析构时释放锁。它们的主要区别在于unique_lock锁机制更加灵活,可以再需要的时候进行lock或者unlock调用,不非得是析构或者构造时。它们的区别可以通过成员函数就可以一目了然。在这里插入图片描述
wait/wait_for说明
线程的阻塞是通过成员函数wait()/wait_for()/wait_until()函数实现的。这里主要说明前面两个函数:
wait()成员函数
函数声明如下:
void wait( std::unique_lock<std::mutex>& lock );
//Predicate 谓词函数,可以普通函数或者lambda表达式
template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate pred );
wait 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,可选地循环直至满足某谓词。
wait_for()成员函数
函数声明如下:
template< class Rep, class Period >
std::cv_status wait_for( std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time);
template< class Rep, class Period, class Predicate >
bool wait_for( std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time,
Predicate pred);
wait_for 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,或者超时返回。
返回值说明:
-
若经过 rel_time 所指定的关联时限则为 std::cv_status::timeout ,否则为 std::cv_status::no_timeout 。
-
若经过 rel_time 时限后谓词 pred 仍求值为 false 则为 false ,否则为 true 。
以上两个类型的wait函数都在会阻塞时,自动释放锁权限,即调用unique_lock的成员函数unlock(),以便其他线程能有机会获得锁。这就是条件变量只能和unique_lock一起使用的原因,否则当前线程一直占有锁,线程被阻塞。
notify_all/notify_one
notify函数声明如下:
void notify_one() noexcept;
若任何线程在 *this 上等待,则调用 notify_one 会解阻塞(唤醒)等待线程之一。
void notify_all() noexcept;
若任何线程在 *this 上等待,则解阻塞(唤醒)全部等待线程。
虚假唤醒
在正常情况下,wait类型函数返回时要不是因为被唤醒,要不是因为超时才返回,但是在实际中发现,因此操作系统的原因,wait类型在不满足条件时,它也会返回,这就导致了虚假唤醒。因此,我们一般都是使用带有谓词参数的wait函数,因为这种(xxx, Predicate pred )类型的函数等价于:
while (!pred()) //while循环,解决了虚假唤醒的问题
{
wait(lock);
}
原因说明如下:
假设系统不存在虚假唤醒的时,代码形式如下:
if (不满足xxx条件)
{
//没有虚假唤醒,wait函数可以一直等待,直到被唤醒或者超时,没有问题。
//但实际中却存在虚假唤醒,导致假设不成立,wait不会继续等待,跳出if语句,
//提前执行其他代码,流程异常
wait();
}
//其他代码
...
正确的使用方式,使用while语句解决:
while (!(xxx条件) )
{
//虚假唤醒发生,由于while循环,再次检查条件是否满足,
//否则继续等待,解决虚假唤醒
wait();
}
//其他代码
....
条件变量使用
在这里,我们使用条件变量,解决生产者-消费者问题,该问题主要描述如下:
生产者-消费者问题,也称有限缓冲问题,是一个多进程/线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个进程/线程——即所谓的“生产者”和“消费者”,在实际运行时会发生的问题。
生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。
同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。
生产者-消费者代码如下:
std::mutex g_cvMutex;
std::condition_variable g_cv;
//缓存区
std::deque<int> g_data_deque;
//缓存区最大数目
const int MAX_NUM = 30;
//数据
int g_next_index = 0;
//生产者,消费者线程个数
const int PRODUCER_THREAD_NUM = 3;
const int CONSUMER_THREAD_NUM = 3;
void producer_thread(int thread_id)
{
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
//加锁
std::unique_lock <std::mutex> lk(g_cvMutex);
//当队列未满时,继续添加数据
g_cv.wait(lk, [](){ return g_data_deque.size() <= MAX_NUM; });
g_next_index++;
g_data_deque.push_back(g_next_index);
std::cout << "producer_thread: " << thread_id << " producer data: " << g_next_index;
std::cout << " queue size: " << g_data_deque.size() << std::endl;
//唤醒其他线程
g_cv.notify_all();
//自动释放锁
}
}
void consumer_thread(int thread_id)
{
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(550));
//加锁
std::unique_lock <std::mutex> lk(g_cvMutex);
//检测条件是否达成
g_cv.wait( lk, []{ return !g_data_deque.empty(); });
//互斥操作,消息数据
int data = g_data_deque.front();
g_data_deque.pop_front();
std::cout << "\tconsumer_thread: " << thread_id << " consumer data: ";
std::cout << data << " deque size: " << g_data_deque.size() << std::endl;
//唤醒其他线程
g_cv.notify_all();
//自动释放锁
}
}
int main()
{
std::thread arrRroducerThread[PRODUCER_THREAD_NUM];
std::thread arrConsumerThread[CONSUMER_THREAD_NUM];
for (int i = 0; i < PRODUCER_THREAD_NUM; i++)
{
arrRroducerThread[i] = std::thread(producer_thread, i);
}
for (int i = 0; i < CONSUMER_THREAD_NUM; i++)
{
arrConsumerThread[i] = std::thread(consumer_thread, i);
}
for (int i = 0; i < PRODUCER_THREAD_NUM; i++)
{
arrRroducerThread[i].join();
}
for (int i = 0; i < CONSUMER_THREAD_NUM; i++)
{
arrConsumerThread[i].join();
}
return 0;
}
运行结果:
- EOF -
1、
2、
3、
关于 C++ 条件变量,欢迎在评论中和我探讨。觉得文章不错,请点赞和在看支持我继续分享好文。谢谢!
关注『CPP开发者』
看精选C++技术文章 . 加C++开发者专属圈子
↓↓↓
点赞和在看就是最大的支持❤️
以上是关于C/C++ 变量详解的主要内容,如果未能解决你的问题,请参考以下文章