使用C很长时间,但是很难说对c的各个点都十分的透彻。虽然c不像c++那样复杂,但是还有很多叽里旮旯儿:并不是他们有多难,而是在于他们平时用的不多,或者和人的第一直觉相悖,再或者初学时经验有限理解不深根本没有记住。
下面的这些东西可能来自《c专家编程》或者网络。最近发现基础的经典的书籍常读常新,原因可能有两个:
1、随着自己经验的增长,你的认识可能会不一样,思维的方式也会有所变化,而得到的东西自然会是新的东西。
2、早些时候经验有限,有些点可能根本就没有完全理解。现在你可以理解的更深刻。
这方面的书籍再比如《代码大全》,前几天翻了一下,又有不同的认识。
进入正题:
1、有符号和无符号的比较:
printf("%d\n", sizeof('A')):打印的值是4(或者是int的长度)而不是1。因为c有类型提升,它会首先把'A'提升为int类型,然后在传给sizeof。表达式中的参数会提升为int或者double,然后在进行运算,之后再进行裁剪,获得指定类型的值。
if (-1 <= sizeof(int)):sizeof的返回值是unsigned int,-1会被类型转换为unsignedint,然后在进行比较。。
这里涉及到的是类型提升,隐式类型转换。它会在表达式中发生,也会在函数入参中发生。
2、枚举在内存中的大小:占四个字节。
3、局部变量也是字节对齐的:
E_T g;
E_T f;
E_T e = false;
char c1;
char c2;
int i1;
char c3;
int i2;
printf("%p, %p, %p, %p, %p, %p, %p, %p\n", &g, &f, &e, &c1, &c2, &i1, &c3, &i2);
--表示是补齐的位。
4、宏定义中的#和##:#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。
而##被称为连接符(concatenator),用来将两个Token连接为一个Token。
5、浮点数不可以用等于比较。
6、void foobar2() 表示函数入参个数有多个,不确定。如果表示没有产生,应该是:void foobar2(void)
7、全局变量会被初始化为0,但是,栈中的局部变量不会被初始化。
8、inline函数和宏:内联函数是真正的函数,但是它是在编译期的优化。
9、 int a[5]; printf("%x\n", a); printf("%x\n", a+1); printf("%x\n", &a); printf("%x\n", &a+1);
最后一个,&a+1,&a表示数组,所以,应该是增加数组大小:4*5个字节。
10、10U表示一个无符号类型的数字10.
11、移位运算的优先级比较低,低于四则运算。
12、左移n位,相当于乘与2的n次方。右移相当于处于2的n次方。
13、指针和数组:
1)、void fun(char buf[100])
{
printf("%d, \n", sizeof(buf));
}
打印的值是4,而不是100。
2)、在一个文件中char p[10] = "";
在另外一个文件中声明:extern char *p;
然后,在声明的文件中sizeof(p),答案是4。也就是,sizeof计算的是声明的类型。
3)对于编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址。
4)所有作为函数参数的数组名编译器都会转换为指针,在其他所有的情况下,数组的声明就是数组,指针的什么就是指针。
数组和指针相同情况的规则:
1、表达式中的数组名(与声明不同)被编译器当作一个指向该数组的第一个元素的指针。
2、下标总是与指针的偏移量相同。
3、在函数的声明中,数组名被编译器当作指向该数组第一个元素的指针。这个操作时编译器完成的。原因是出于效率的考虑。因为这样就是引用传递而非值传递。值 传递需要拷贝。这也可以看的出sizeof是在汇编中操作的。
arry[-1]的行为是未定义的。
总结:
1)a[i]这样的形式对a进行访问,总是被编译器改写为像*(a+i)的形式。
2)指针始终是指针,你不可以把它改写成数组,但是可以通过数组的形式访问。
3)数组作为函数的参数,会被编译器改写成指针。
4)指针和数组的什么必须配对。
14、声明与定义:声明可以由多个,定义只有一个。定义是特殊的声明,它为对象分配了内存。而声明时普通的声明,描述其他地方创建的对象。
声明的优先级规则:
a:从他的名字开始按照优先次序依次读取:
b:优先级的高低:
1、声明中被括号括起来的那部分。
2、后缀操作符:
括号()表示是一个函数;
方括号[]表示是一个数组;
3、前缀操作符:*表示指向什么的指针;
4、const紧跟变量则修饰变量不可修改,紧跟类型则指向的东西不可修改。
15、多维数组:
a[2][3]:a是一个数组,有两个元素。每个元素又是一个数组,有三个元素。
内存布局:a[0][0],a[0][1],a[0][2],a[1][0]...地址一直变大。
多维数组,数组的数组作为函数的形参,会被转化为数组指针,数组的指针,也是行指针。本质上也是指针。
16、结构体默认的字节对齐一般满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员自身大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
基于c语言知识点的补遗介绍
2020-12-03 10:38C语言教程网 C/C++
本篇文章是对c语言知识点的一些补遗进行详细的分析介绍,需要的朋友参考下
延伸 · 阅读
- 2022-03-11C语言练习之数组中素数交换
- 2022-03-11C语言实现将double/float 转为字符串(带自定义精度
- 2022-03-11c语言单词本的新增、删除、查询按顺序显示功能
- 2022-03-11C语言实现自动发牌程序代码
- 2022-03-11C语言中单目操作符++、–的实例讲解
- 2022-03-11C语言中数据在内存如何存储
- C/C++
C++输入一个字符串,把其中的字符按照逆序输出的两种方法解析
以下是对C++中输入一个字符串,把其中的字符按照逆序输出的两种方法进行了详细的分析介绍,需要的朋友可以过来参考下...
- C/C++
构造函数不能声明为虚函数的原因及分析
构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问...
- C/C++
深入分析C++中deque的使用
本篇文章介绍了,深入分析C++中deque的使用。需要的朋友参考下...
- C/C++
Opencv绘制最小外接矩形、最小外接圆
这篇文章主要为大家详细介绍了Opencv绘制最小外接矩形、最小外接圆的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以...
- C/C++
Visual Studio C++指针靠前靠后的问题全面解析
这篇文章主要介绍了Visual Studio C++指针靠前靠后的问题全面解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可...
- C/C++
如何用C语言、Python实现栈及典型应用
本文先通过实例分别介绍了如何用C语言、Python实现栈,后又介绍栈的典型应用,对大家学习栈很有借鉴参考价值,下面一起来看看吧。...
- C/C++
C语言实现随机抽奖程序
这篇文章主要为大家详细介绍了C语言实现随机抽奖程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...
- C/C++
浅谈防不胜防的unsigned int的运算
下面小编就为大家带来一篇浅谈防不胜防的unsigned int的运算。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...