C语言学习 — 基础知识细节说明
最近重新学习了一下C语言教学视频看了看,把一些知识点细节记录一下。
1、变量内存区域
在现代计算机系统中,物理内存被分为不同区域
区域不同,用途不同,不同种类的变量位于不同的区域
- 全局数据区: 存放全局变量,静态变量
- 栈空间: 存放函数参数,局部变量
- 堆空间:用于动态创建变量
static修饰的局部变量
int global;
int func(int x)
{
static int var;
var += x;
return var;
}
int main()
{
int i =0;
for(i=0;i<=5;i++)
printf("func(%d) = %d\n",fun(i));
printf("func(0)=%d",fun(0));
printf("global = d%\n",global);
return 0;
}
上面的例子中,var 被static 修饰了,所以var 变量 存储于 全局数据区
static int var; 没有初始化数值,
全局数据中的变量,默认初始化为0
函数返回var的值,虽然是一个局部变量,但是var的值依然保存着返回时候var的值
var只做一次初始化
2、函数相关
下面的额函数调用了几个参数
int main()
{
func(((1,2),(3,4,5)));
return 0;
}
答案是 1个参数,注意括号
而且函数的调用,是可以使用表达式(都好表达式,以逗号最后一个参数为准,具体看例子):
int func(int var)
{
var++;
return var;
}
int main()
{
int r = func(((1,2),(3,4,5)));
printf("r= %d\n",r);
return 0;
}
上面的函数执行输出的结果为 6 ,其实就是 5++ ,逗号表达式从左至右计算结果, 结果为6
再来看一个例子
int func(int a, int b)
{
return a+b;
}
int main()
{
int x = 6,y = 7, z = 8;
int r = 0;
r = func((x--, y++, x+y), z--);
printf("%d\n",r);
return 0;
}
函数的调用使用了 func((x–, y++, x+y), z–) 这个,是可以的,其中参数 a 是(x–, y++, x+y)表达式的结果,逗号表达式总左至右计算,5,8, 5+8 =13 z先取值8 ,再 --, 所以结果是21
我们知道,实参只是用来初始化形式参数,并不会改变实参的值,相当于在调用的时候赋值给形式参数,所以本身实多少还是多少,与函数中的运算无关
关于数组元素作为函数的参数,是会改变素组元素中的值得,和其他的形参不一样,是不是可以理解为对数组的操作其实就是对指针指向的地址参数的操作,具体分析请看后面的内容,指针部分的解析。
3、逗号表达式
不良示例:
#include<stdio.h>
void hello()
{
printf("hello!\n");
}
int main()
{
int a[3][3] ={ //{2,5,8},前3个元素 2,5,8,后面全部为0
(0,1,2),
(3,4,5),
(6,7,8),
};
int i = 0;
int j = 0;
while(i < 5)
printf("i = %d\n",i),
hello(),
i++;
for(i=0; i<3; j++)
{
for(j=0; j<3; j++)
{
printf("a[%d][%d] = %d\n",i,j,a[i][j]);
}
}
return 0;
}
一行代码实现strlen
#include<stdio.h>
#include<assert.h>
int strlen(const char* s)
{
return assert(s),(*s ? strlen(s + 1) + 1 : 0);
}
int main()
{
printf("len = %d\n",strlen("qianzhiheng"));
printf("len = %d\n",strlen(NULL));
return 0;
}
4、补充基本知识
负数 无符号数相关
数据类型的最高位用于标识数据的符号:
-
最高位为1,表明这个数为负数
-
最高位为0,表明这个数为正数
-
计算机用补码标识有符号数,正数的补码为正数本身,负数的补码为绝对值各位取反+1
-
无符号数默认为正数,没有符号位
-
对于固定长度的无符号数
最大值 + 1 = 最小值
最小值 - 1 = 最大值 -
C语言中只有 整数类型 能够声明 unsigned 变量
-
当无符号数与有符号数混合计算时,会将有符号数转换为无符号数后再计算,结果为无符号数
-
char类型 和 short类型运算,会隐式转化为int类型;
浮点数
- int类型范围:[ -2^31 ~ 2^31 - 1]
- float类型范围:[ -3.4E+38 ~ 3.4E+38]
- int 和 float 都占 4个字节的内存,他们所能表示的数据的个数是一致的,但是float 比int 范围大得多, 表示的数据个数一致,所以float表示的数据不是连续的,所以说float是不精确的,同理 double也是类似,只是他比float 精度高一些
do 和 break的妙用:
#include <stdio.h>
#include <malloc.h>
int func(int n){
int i = 0;
int ret = 0;
int *p = (int*)malloc(sizeof(int*) * n);
do{
if( NULL == p ) break;
if( n < 5 ) break;
if( n > 100 ) break;
for(i=0; i<n; i++){
p[i] = i;
printf("%d\n",p[i]);
}
ret = 1;
}while(0);
printf("free(p)\n");
free(p);
return ret;
}
int main()
{
if(func(1000))
{
printf("OK!\n");
}else{
printf("ERROR\n");
}
return 0;
}
void*
void* 类型的指针可以接受任意类型的指针值
const 和 volatile
const 修饰的变量不是真的常量,它只是告诉编译器该变量不能出现在赋值符号的左边
- const 修饰的变量是只读的,本质还是变量
- const 修饰的局部变量 在 栈 上分配空间
- const 修饰的全局变量 在 全局数据区 分配空间
- const 只在编译期有用,在运行期无用
- 现代的C编译期中的const将 具有全局生命周期 的变量存储于只读存储区(比如gcc,看下面例子,编译通过,但是执行会出错,) static 修饰的变量也具有全局生命周期
- const 修饰的普通 局部变量 是在栈上分配空间的,所以下面的例子成功修改了局部变量cc的值
- 对于不该被修改的入参,应该用const修饰,这是const使用的常见姿势。
- const修饰的变量只能正常赋值一次。
- 不要试图将const数据的地址赋给普通指针。
- 虽然可以通过某种不正规途径修改const修饰的变量,但是永远不要这么做。
#include<stdio.h>
const int g_cc = 2;
int main()
{
const int cc =1;
int *p = (int*)&cc;
printf("cc= %d\n",cc);
*p = 3;
printf("cc= %d\n",cc);
printf("g_cc= %d\n",g_cc);
p =(int*)&g_cc;
*p = 4;
printf("g_cc= %d\n",g_cc);
return 0;
}
- volatile 可理解为 “编译期警告指示字”
- volatile 告诉编译期必须每次去内存中取变量值
- volatile 主要修饰可能被多个线程访问的变量
- volatile 也可以修饰可能被未知因素更改的变量
int ob = 10;
int a = 0;
int b = 0;
a = ob;
delay(100);//如果ob在其他线程或者中断中修改了值,就会出问题
b = ob;
const volatile int i = 0;//定义了一个只读变量i,每次都得去内存中取值,不让编译期进行优化
sizeof
sizeof 是C语言的内置关键字而不是函数
- 在编译过程中所有的 sizeof 将被具体的数值所替换
- 程序的执行与 sizeof 没有任何的关系
int var = 0;
int size = sizeof(var++);
printf("%d, %d\n",var,size); // 0,4
int func()
{
printf("sdasdasda\n");
}
int main()
{
size = sizeof(func()); //这个不是调用函数,所以func()中的打印不会执行
printf("%d\n",size); // 4
}
逻辑运算符分析
-
|| 从 左向右开始计算:
当遇到为 真 的条件时 停止计算,整个表达式为 真
-
&& 从 左向右开始计算:
当遇到为假的条件时停止计算,整个表达式为 假
-
逻辑表达式中,虽然 && 比 || 具有更高的优先级,但是并不是说 && 先计算,而是如下解释:
-
运算优先级: 四则运算 > 位运算 > 逻辑运算 (实际项目中用括号)
#include <stdio.h>
int main()
{
int i=0,j=0,k=0;
++i || ++j && ++k;
printf("%d\n",i);
printf("%d\n",j);
printf("%d\n",k);
return 0;
}
++ 和 - - 问题
- ++ 和 – 参与混合运算的结果是不确定的,编译器不同,可能结果不同
- 在混合运算中, ++ 和 – 的汇编指令可能被打断执行
int i = 0;
(i++) + (i++) + (i++);
(++i) + (++i) + (++i);
/*求i的值和表达式的结果,在不同编译器下运行的结果不一样,实际项目不要这么写*/
所以实际项目中 多使用 空格 是个好习惯
空格可以作为C语言一个完整符号的休止符,编译器读入空格后立即对之前读入的符号进行处理
int main()
{
int i = 0;
int j = ++i+++i+++i; //编译器一直读,读到 ++i还会继续 读到++i+ 编译器继续往下读,\
如果读到一个变量,还会继续读,但是编译器读到++i++,编译器认\
为不管下面读到什么,也不能组成有意义的符号,所以读到++i++就\
停止去处理,处理后就是 ++i++ => 1++; 所以这里就出问题了,\
++i + ++i + ++i;加上空格解决
int a = 1;
int b = 4;
int c = a+++b;//编译器读到 a+++b; 能够一直读完再处理 a++ + b;
int* p = &a;
b = b/*p; //这里写法也会被编译器误认为后面是注释,编译器一直读,打一个空格 \
b = b / *p; 就能够正常
}
- 点赞
- 收藏
- 关注作者
评论(0)