C语言诞生于1972年11月,由美国电话电报公司(AT&T)贝尔实验室的丹尼斯·里奇(Dennis Ritchie)发明,在1978年,美国电话电报公司(AT&T)贝尔实验室正式发布了C语言。C语言作为一种经典的编程语言,时至今日,仍然在许多领域仍然被广泛使用,并且仍然是一种强大、高效和灵活的编程语言。
学习C语言对于初学者来说可能有一定的难度,因为C语言是一种比较底层的编程语言,它提供了更接近计算机硬件的控制能力。与一些更高级的编程语言相比,C语言在语法和概念上可能更加复杂,需要更多的时间和精力来学习和掌握。从本文开始,我会分享一些C语言的细节冷知识,希望对大家有所帮助。
数组初始化可以说是非常简单也很常用的操作,一般来说包括以下几种方式:
1、定义时初始化
数组在定义时就进行初始化,这个比较常见,最常用在把数组元素全部初始化为0的时候。
比如,以下代码会将数组a的所有元素都初始化为0:
int a[10] = {0};
拓展一下:
int a[10] = {1};
上述代码中,初始化后的数组a是不是所有元素均为1呢?答案是否定的。只有a[0]会被初始化为1,而a[1~9]会被默认初始化为0,这点需要注意。
2、按字节初始化
对数组按照字节进行初始化,主要是使用memset函数进行,主要使用场景是数组元素被初始化为同样的值。
比如:
unsigned char a[100];
memset(a,123,sizeof(a));
上述代码中,初始化后的数组a[0~99]均被初始化为123。
3、循环遍历初始化
用循环遍历的方式对数组进行初始化,主要使用场景是数组元素被初始化为非0,且初始值有一定的规律的时候。
比如:
int i;
int a[10];
for( i = 0; i < 10; i++ )
{
a[i] = i + 1;
}
上述代码中,初始化后的数组a[0~9]分别被初始化为1 ~ 10。
注意以上每种数组初始化方式的应用场景。一般来说,如果是将所有数组元素均初始化为0,推荐使用第一种方式(书写简单,执行效率也高);如果不嫌书写麻烦的话,第二种方式也可以,执行效率相差不大;第三种方式的循环遍历就不推荐了,如果数组很长,循环遍历会执行很长的时间,不到万不得已,不要使用。
在C语言中,volatile关键字用于告诉编译器该变量可能会在任何时候被意想不到地改变,因此,编译器在生成代码时不应对此变量进行优化,使得在用到这个变量时必须每次都重新从内存中读取这个变量的值,而不是使用保存在寄存器里的备份。
想一想以下代码有什么问题:
//求平方
int square(volatile short *ptr)
{
if( ptr == NULL )
{
return -1;
}
return (*ptr) * (*ptr);
}
ptr被volatile修饰,ptr的值可能被意想不到地改变,因此前一个ptr和后一个*ptr可能是不同的值,计算出来就可能不是平方值。要想达到求平方的目的,应做如下修改:
//求平方
int square(volatile short *ptr)
{
int tmp;
if( ptr == NULL )
{
return -1;
}
tmp = *ptr;
return tmp * tmp;
}
C语言中static关键字的主要作用有:限制变量和函数的作用域;定义静态变量;在函数中保持变量的值等。需要特别注意的就是在函数内部定义的局部静态变量,和普通的局部变量有着很大的不同,局部静态变量只会被初始化一次,此后其值会一直保持,不会像普通的局部变量一样,每次函数调用都会重新定义。
int sum_fun(int a)
{
static int b=1;
int c = 1;
b+=1;
return(a+b+c);
}
void main()
{
int i;
int a=2;
for(i=0;i<5;i++)
{
printf("%d,", sum_fun(a));
}
}
执行上述代码会输出:5、6、7、8、9
在C语言中,被const修饰的变量不可修改,意味着“只读”。
思考一下以下代码中的定义有什么区别:
const int a; //1
int const a; //2
const int *a; //3
int * const a; //4
const int *const a; //5
1和2的作用是一样,都表示a是一个常整型数。
3表示a是一个指向常整型数的指针(整型数不可修改,但指针可以修改)。
4表示a是一个指向整型数的常指针(指针指向的整型数可以修改,指针不可修改)。
5表示a是一个指向常整型数的常指针(指针指向的整型数和指针本身均不可修改)。
宏定义在C语言被广泛使用,主要用于为代码中的常量、函数或复杂的表达式定义一个简化的名称。但是宏定义只是简单的文本替换,它不进行任何类型检查,因此,在使用宏定义时需要特别小心,以避免产生不易察觉的错误。
想一想以下代码有什么问题:
#define abs(x) x >= 0 ? x : -x
上述代码在执行“c = abs(a-b);”语句时,宏替换后变成“c = a-b >= 0 ? a-b : -a-b;”,当a-b小于0时,结果错误。应做如下修改:
#define abs(x) (((x) >= 0) ? (x) : -(x))
可见懂得把宏定义参数用括号括起来是多么的重要。
以上都是一些C语言的基础,但却又是平时编程中很容易因为疏忽出现问题的地方。更多的细节冷知识将在后续系列文章中持续更新,希望对大家有所帮助。