《C语言知识点》
https://wenku.baidu.com/view/5bf31284ba0d4a7302763a8a.html
1. #define定义的宏和const定义的常量有什么区别?
1、两者的区别 (1) 编译器处理方式不同 #define 宏是在预处理阶段展开。 const 常量是编译运行阶段使用。 (2) 类型和安全检查不同 #define 宏没有类型,不做任何类型检查,仅仅是展开。 const 常量有具体的类型,在编译阶段会执行类型检查。 (3) 存储方式不同 #define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。) const定义的变量属于只读变量。是有分配内存的。
注意:只读变量和常量是有区别的。常量肯定是只读的,例如5, “abc”,等,肯定是只读的,因为程序中根本没有地方存放它的值,当然也就不能够去修改它。而“只读变量”则是在内存中开辟一个地方来存放它的值,只不过这个值由编译器限定不允许被修改。
其中:int a[n]。数组的大小n应该是个常量。所以用const int n = 5。这个是不行的。
例程:
#define N 2 + 3
int a = N / 2;
a是多少?
int a = 2 + 3 / 2 因为a为int型,所以不能有小数点 所以a = 3
const char *pContent; //*pContent是const, pContent可变
const (char *) pContent;//pContent是const,*pContent可变
char* const pContent; //pContent是const,*pContent可变
const char* const pContent; //pContent和*pContent都是const
2. 给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a的bit2,在以上两个操作中, 要保持其它位不变。
#define BIT3 (0x1 << 3) static int a; void set_bit3(void) { a |= BIT3; } void clear_bit3(void) { a &= ~BIT3; } 最上面的括号一定要加上,不然在clear_bit3中会有问题,~的优先级问题
3. char str[20]="0123456789";
int len1=strlen(str);
int len2=sizeof(str);
len1和len2分别是什么值?
len1 = 10; strlen计算字符串长度,碰到\0就停止计数 len2 = 20,; sizeof计算的是数组内存长度。所以str[20]改为str[],那么len2 = 11;他会计算包括null这个
4. 请问int * p 和 char * p分别占几个字节?为什么?
在32位系统中,都是4个字节。
因为计算的是指针的长度,至于int和char,指的是指针所指向内存的数据类型。
5. 下面的代码使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
1、ISR不应该有返回值; 2、ISR不应该传递参数; 3、ISR应该是短而高效的,ISR中的浮点运算是不明智的; 4、ISR中不应该使用printf()函数,printf是不可重入函数(malloc函数也不能用)。
为什么ISR不能有返回值和传参?
因为对于ISR是由硬件触发的,因此没有调用者。
为什么ISR不能用printf和malloc函数?
首先因此这两个函数都是不可重入函数。
printf --------引用全局变量stdout
malloc --------全局内存分配表
而中断是随机发生的,如果先使用printf函数,此时中断来临,最后中断里面又使用了printf,那么此时就会改变全局变量stdout的值,也就是最后输出到屏幕的值不是自己所期望的。
6.关键字static的作用是什么?
1.修饰全局变量时,改变该变量的文件作用域,在其他文件不可调用。 2.修饰局部变量时,改变该变量存储方式,该变量在全局数据区分配内存。但是变量的作用域不变。 3.修饰函数时,改变该函数的文件作用域,其他文件不可调用。
7.用+、-、++、--实现a/5,a为一整数,如a=10,则返回2,a为21,则返回4。
int fun(int value) { int ret, i; ret = 0; if(value > 0) { while(1) { for(i = 0; i < 5; i++) { value--; if(value == 0) { return ret; } } ret++; } } else if(value == 0) { ret = 0; } else { ret = -1; } return ret; }
8.可重入函数和不可重入函数
《可重入函数和不可重入函数》 - 一个不知道干嘛的小萌新 - 博客园 (cnblogs.com)
在多线程中,尤其要特别主要这个概念。对资源要有保护机制。
9.定义一个标准宏MAX,总是输出最大值
#define MAX((a),(b)) ((a)>(b))?(a):(b) 注意在使用时,尽量不要传入自增++或自减—表达式,否则将会导致多次运算
10.关键字extern、static、const、volatile分别是什么含义?
extern 说明变量是在其他文件中定义的,为了达到不包含头文件也可以使用其他文件中的变量 或者 extern “C” 来完成C 和 C++的相互引用 static 改变变量或函数的作用域或生存周期,当修饰全局变量和函数时,改变其作用域为仅在本文件中,其他文件不可以适用;当修饰局部变量时,扩展其生存周期至本程序结束,并且只被初始化一次。 const 改变变量的读写权限至只读 volatile 告诉编译器该变量随时有可能会变化,不要试图在编译的过程中对齐优化;同时在程序运行的过程中,每一次的访问都应该实际的从主存或寄存器中去读。一般用在多线程或中断处理程序中。
11.要求设置一绝对地址为0x67a9的整型变量的值为0xaa66
访问一绝对地址把一个整型数强制转换成一个指针。 int *ptr; ptr =(int *)0x67a9; *ptr= 0xaa66;
12.下面的代码输出是什么,为什么?
void func(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6)?puts(">6"):puts("<= 6");
}
输出>6 C中运算有规定,如果整型变量间进行数据运算,只要有一个变量是无符号的,结果就按无符号数据
13.以下两行代码那个实现方式更好?为什么?
#define dPS struct s *
typedef struct s * tPS;
第二种。 #define 只是简单的在预处理阶段将字符串进行展开 而typedef则是定义了一个别名 举例: dPS c,d; 实际展开后成为 int *c,d 表示定义了一个整形指针c 和 整形变量 d tPS c,d 而 tPS则是一个整体,他代表的就是int *这个类型,所以定义的是两个整形指针c和d
14.写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的?
1. 中断服务内的执行过程尽量短,并且不去操作硬件资源 2. 中断服务函数没有传参和返回值 3. 中断服务函数内的程序应该是可重入的,不能使用printf 4. 中断服务函数内不应做浮点运算这一类的复杂运算。 5. 中断服务函数内尽量不要使用锁(互斥锁和自选锁) 当需要做较多的事情时: 可以中断的发生和事情的处理拆分开来,中断服务程序内只做状态的修改,然后从其他程序中去对相应的状态做不同的处理。 使用 消息 或 volatile 声明的变量来做状态的传递
15.堆和栈的区别?
1.申请方式、栈的空间由操作系统自动分配以及释放。堆上的空间需要手动分配和释放(malloc以及amalloc等的区别?) 2.申请大小。堆的可用空间比较大,栈的可用空间比较小,一般是2M。 3.申请效率。栈的申请速度比较慢,堆的申请速度比较快。
16. 为什么栈的空间不连续 ?
17.死循环的几种方式
1. while(1) { ; } 2. for(; ;) { ; } 3 LOOP: ...... goto LOOP; 4. do { ; }while(1);
18.左值和右值
左值可写,右值可读。通常,左值可以作为右值,但是右值不一定是左值。
左值可能是变量,变量又可以作为右值。但是右值可能是常量,常量不能作为左值。
19.数组名和指针的区别?
1.指针是一个变量,而数组名不是。数组名是数组的首地址,即它本身就是一个地址。 2.假设a是一个数组名,而p是一个指针,当你使用 a 和 &a 时,得到值是一样的,都是数组的起始地址。而使用 p 和 &p 时,得到的值是不一样的, p 表示指针 p 所指向的地址,而 &p 表示 p 这个变量的地址。 3.对数组的引用,如a[i],或*(a+1),需要访存一次;而对指针的引用,如*(p+1),需要访存两次。 如果理解了第二条的解释,这个应该就不难理解。因为a被认为是常数,所以取*(a+1)的值只需将a所表示的常数加1,然后从得到的地址里访存取一次即可。而对于指针,需要先从&p这个地址里把p的值取出来,然后加1,再从得到的地址里访存取一次,一共需要两次访存。
20.指针函数,函数的参数为int,返回值为字符指针
char *((*p)(int))
以上只是一个函数声明。所以有用到函数指针的概念。
21.typedef和define有什么区别
typedef定义指针的别名时,别名可以连续定义两个指针变量。define定义指针的别名是,使用这个别名连续定义两个指针变量会报错。
22.数组下标可以用负数吗?
23.不能用sizeof函数,如何判断操作系统是16位还是32位?
方法1: 16位系统: int i = 65536; cout << i; // 输出0;//装不下,最高位溢出,剩下16位的当然是0; int i = 65535; cout << i; // 输出-1;//-1的补码是65535 32位系统: int i = 65536; cout << i; // 输出65536; int i = 65535; cout << i; // 输出65535; 方法2: int a = ~0;//按位取反运算,结果为(11111111111111111111111111111111) if( a>65536 ) { cout<<"32 bit"<<endl; } else { cout<<"16 bit"<<endl; }
24.什么是4字节对齐?为什么需要对齐?
字节对齐是为了提高存取效率,并且一般是偶个字节对齐(2 4 6 8……),因为总线的位数都是偶数的(8 32 64……),并且每个周期的周期都是从偶地址开始访问的,若不是没有偶字节对齐,在某些情况(比如若某变量的内存空间为0x33 ~ 0x36)下访问一块内存将会多耗费一个周期。
4字节对齐就是32位。也就是常规的32位总线。对应我CPU每次去访问内存最大可以一次性访问32位。
比如定义一个char a[1] = 'a';这个正常只需要8位的存储,如果4字节对齐,就变成32位存储。
这个时候printf("%c",a[0]);和printf("%d",a[0]);就会出现前者是取了a[0]的前8位,后者是取了a[0]的32位。
25.怎么计算结构体所占内存?
26.什么是野指针?如何避免?
27.sizeof和strlen的区别?
sizeof是运算符,在程序编译时就已经确定了;
strlen是函数,程序运行时才能计算。
29.Int a[5] = {1,2,3,4,5} sizeof(a) = ?
20
30.scanf和gets的有何利弊
用scanf()函数输入字符串时,默认分隔符是空格、跳格(Tab)等,
因此scanf()函数不能输入含有上述字符的字符串,这是其不足之处;
与gets()相比,其优点是它可以一次输入多个字符串,而且还可以用于输入不同类型的数据,应用面较广。
用gets()函数输入时,可以输入含空格、跳格等字符的字符串,但其不足之处在于,它只能用于输入字符串,且一次只能输入一个。
31.程序编译过程
预处理:预处理相当于根据预处理命令组装成新的C程序,不过常以i为扩展名。
编译: 将得到的i文件翻译成汇编代码.s文件。
汇编:将汇编文件翻译成机器指令,并打包成可重定位目标程序的O文件。该文件是二进制文件。
链接:将引用的其他O文件并入到我们程序所在的o文件中,处理得到最终的可执行文件。
32.怎么判断大端小端
方法1:利用联合体
关键点:联合体的存放顺序是所有成员都从低地址开始存放。
void check_cup(void) { union{ short s; char c[sizeof(short)]; }un; un.s = 0x0102; if (un.c[0] == 1 && un.c[0] == 2) puts("Big endian."); else if (un.c[0] == 2 && un.c[0] == 1) puts("Little endian."); else puts("Unkown"); }
方法2:利用强制类型转换
void check_cup(void) { int a = 0x0102; char *p = (char *)&a; if (*p == 2) puts("Big endian."); else if (*p == 1) puts("Little endian."); }
memcpy