关键字
- C语言宏中“#”和“##”的用法
- 关键字volatile有什么含义?并举出三个不同的例子?
- 关键字static的作用是什么?
- 在C语言中,为什么static变量只初始化一次?
- extern ”C“的作用是什么?
- const有什么作用?
- 什么情况下使用const关键字?
- new/delete与malloc/free的区别是什么?
- strlen("\\0") = ? sizeof("\\0") = ?
- sizeof与strlen有什么区别?
- 不使用sizeof,如何求int占用的字节数?
- C语言中struct与union的区别是什么?
- 左值和右值是什么?
- 什么是短路求值?
- ++a和a++有什么区别?两者是如何实现的?
C语言宏中“#”和“##”的用法
1.“#”- 字符串化操作符
作用:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。
例:
#define text_1(str)printf("the input string is :\\t%s\\n",#str)
#define text_2(str)#str
当使用该宏定义时:
text_1(abc);//在编译时将会展开成:printf("the input string is :\\t%s\\n","abc")
string cstr = text_2(abc);//将会展成:string cstr = "abc"
2.“##”- 符号连接操作符
作用:将宏定义的多个形参转换成一个实际参数名。
例:
#define text(n)text_##n
使用:
int num = text(str);//将会扩展成 int num = text_str
注意: a.当用##连接形参时,##前后的空格可有可无。
如:
#define test(n)num ## n
//相当于 #define test(n)num##n
b.连接后的实际参数名,必须为实际存在的参数名或是编译器已知的宏定义。
c. 如果##后的参数本身也是一个宏的话,##会阻止这个宏的展开。
#include <stdio.h>
#include <string.h>
#defineSTRCPY(a,b)strcpy(a ## _p, #b)
int main()
{
char var1_p[20];
char var2_p[30];
strcpy(var1_p, "aaaa");
strcpy(var2_p, "bbbb");
STRCPY(var1, var2);
STRCPY(var2, var1);
printf("var1 = %s\\n", var1_p);
printf("var2 = %s\\n", var2_p);
//STRCPY(STRCPY(var1,var2),var2);
//这里是否会展开为: strcpy(strcpy(var1_p,"var2")_p,"var2“)?答案是否定的:
//展开结果将是: strcpy(STRCPY(var1,var2)_p,"var2")
//## 阻止了参数的宏展开!如果宏定义里没有用到 # 和 ##, 宏将会完全展开
// 把注释打开的话,会报错:implicit declaration of function 'STRCPY'
return 0;
}
结果:
var1 = var2
var2 = var1
关键字volatile有什么含义?并举出三个不同的例子?
XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;
对外部硬件而言,上述四条语句分别代表不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58;(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,编译器会逐一的进行编译并产生相应的机器代码(产生四条代码)。
关键字static的作用是什么?
在C语言中,为什么static变量只初始化一次?
对于所有的对象(不仅仅是静态对象),初始化都只有一次,而由于静态变量具有“记忆”功能,初始化后,一直都没有被销毁,都会保存在内存区域中,所以不会再次初始化。存放在静态区的变量的生命周期一般比较长,它与整个程序“同生死、共存亡”,所以它只需初始化一次。而auto变量,即自动变量,由于它存放在栈区,一旦函数调用结束,就会立刻被销毁。
extern ”C“的作用是什么?
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。
const有什么作用?
1.定义变量(局部变量或全局变量)为常量。例如:
const int N = 100;//定义一个常量N
N = 50;//错误,常量的值不能被修改
const int n;//错误,常量在定义的时候必须初始化
2.修饰函数的参数,表示在函数体内不能修改这个参数的值。
3.修饰函数的返回值。
- a.如果给用 const修饰返回值的类型为指针,那么函数返回值(即指针)的内容是不能被修改的,而且这个返回值只能赋给被const修饰的指针。例如
const char GetString();//定义一个函数
char *str = GetString();//错误,因为str没有被const修饰
const char *str = GetString(); //正确
- b.如果用 const修饰普通的返回值,如返回int变量,由于这个返回值是一个临时变量,在函数调用结束后这个临时变量的生命周期也就结束了,因此把这些返回值修饰为 const是没有意义的。
4.节省空间,避免不必要的内存分配。例如:
#define PI 3.14159//该宏在预处理阶段进行文本替换,无内存分配,仅为字面量替换
const double pi = 3.14159;//const修饰的double类型常量pi,定义时多数编译器不会立即分配内存(未放入只读存储区)
double i = pi;//首次使用const变量pi,为其分配内存(通常在数据段),后续使用该内存地址的值
double I = PI;//使用宏PI,编译预处理阶段直接替换为3.14159,为I分配内存存储该字面量值
double j = pi;//复用已分配内存的const变量Pi,直接读取其内存值,无新内存分配,也无宏替换
什么情况下使用const关键字?
1.修饰一般变量。一般变量是指简单类型的常量。这种变量在定义时,修饰符const可以用在类型说明符前,也可以用在类型说明符后。例如:
int const x = 2;
const int x = 2;
2.修饰常数组。定义或说明一个常数组可以采用如下格式:
int const a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
const int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
3.修饰常对象。常对象是指对象常量,定义格式如下:
class A:
const A a:
A const a:
定义常对象时,同样要进行初始化,并且该对象不能再被更新。修饰符 const可以放在类名后面,也可以放在类名前面。
4.修饰常指针
const int*p; //常量指针,指向常量的指针。即p指向的内存可以变,p指向的数值内容不可变
int const*p; //同上
int*const p;//指针常量,本质是一个常量,而用指针修饰它。 即p指向的内存不可以变,但是p内
存位置的数值可以变
const int* const p;//指向常量的常量指针。即p指向的内存和数值都不可变
5.修饰常引用。被 const修饰的引用变量为常引用,一旦被初始化,就不能再指向其他对象了。
6.修饰函数的常参数。 const修饰符也可以修饰函数的传递参数,格式如下:
void Fun(const int Var);
告诉编译器Var在函数体中不能被改变,从而防止了使用者一些无意的或错误的修改。
7.修饰函数的返回值。 const修饰符也可以修饰函数的返回值,表明该返回值不可被改变,格式如下:
const int FunI();
const MyClass Fun2();
8.在另一连接文件中引用 const常量。使用方式有
extern const int 1;
extern const int j=10;
new/delete与malloc/free的区别是什么?
1.new、delete是C++中的操作符,而malloc和free是标准库函数。
2.对于非内部数据对象来说,只使用malloc是无法完成动态对象要求的,一般在创建对象时需要调用构造函数,对象消亡时,自动的调用析构函数。而malloc free是库函数而不是运算符,不在编译器控制范围之内,不能够自动调用构造函数和析构函数。而NEW在为对象申请分配内存空间时,可以自动调用构造函数,同时也可以完成对对象的初始化。同理,delete也可以自动调用析构函数。而mallloc只是做一件事,只是为变量分配了内存,同理,free也只是释放变量的内存。
3.new返回的是指定类型的指针,并且可以自动计算所申请内存的大小。而 malloc需要我们计算申请内存的大小,并且在返回时强行转换为实际类型的指针。
strlen(“\\0”) = ? sizeof(“\\0”) = ?
strlen(“\\0”) =0,sizeof(“\\0”)=2。 strlen用来计算字符串的长度(在C/C++中,字符串是以"\\0"作为结束符的),它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描直到碰到第一个字符串结束符\\0为止,然后返回计数器值;sizeof是C语言的关键字,它以字节的形式给出了其操作数的存储大小,操作数可以是一个表达式或括在括号内的类型名,操作数的存储大小由操作数的类型决定。
sizeof与strlen有什么区别?
strlen与 sizeof的差别表现在以下5个方面。
sizeof是运算符(sizeof既是关键字,也是运算符,但不是函数),而strlen是函数。 sizeof后如果是类型,则必须加括弧,如果是变量名,则可以不加括弧。
sizeof运算符的结果类型是 size_t,它在头文件中 typedef为 unsigned int类型。该类型保证能够 容纳实现所建立的最大对象的字节大小。
sizeof可以用类型作为参数, strlen只能用char*作参数,而且必须是以“0结尾的。 sizeof还可以以函数作为参数,如int g(),则 sizeof(g())的值等于 sizeof( int的值,在32位计算机下,该值为4。
大部分编译程序的 sizeof都是在编译的时候计算的,所以可以通过 sizeof(x)来定义数组维数。而 strlen则是在运行期计算的,用来计算字符串的实际长度,不是类型占内存的大小。例如, char str[20] = "0123456789”,字符数组str是编译期大小已经固定的数组,在32位机器下,为sizeof(char)*20=20,而其 strlen大小则是在运行期确定的,所以其值为字符串的实际长度10。
当数组作为参数传给函数时,传递的是指针,而不是数组,即传递的是数组的首地址。
不使用sizeof,如何求int占用的字节数?
#include <stdio.h>
#define MySizeof(value) (char *)(&value+1)–(char*)&value
int main()
{
int i;
double f;
double a[4];
double *q;
printf("%d\\r\\n",MySizeof(i));
printf("%d\\r\\n",MySizeof(f));
printf("%d\\r\\n",MySizeof(a));
printf("%d\\r\\n",MySizeof(q));
return 0;
}
输出为:
Output: 4 8 32 4
上例中,(char*)& Value 返回 Value的地址的第一个字节,(char*)(& Value+1)返回value的地址的下一个地址的第一个字节,所以它们之差为它所占的字节数。
C语言中struct与union的区别是什么?
struct(结构体)与 union(联合体)是C语言中两种不同的数据结构,两者都是常见的复合结构,其区别主要表现在以下两个方面。
举个例子。下列代码执行结果是多少?
typedef union {double i; int k[5]; char c;}DATE;
typedef struct data( int cat; DATE cow;double dog;)too;
DATE max;
printf ("%d", sizeof(too)+sizeof(max));
假设为32位机器,int型占4个字节, double型占8个字节,char型占1个字节,而DATE是一个联合型变量,联合型变量共用空间,uion里面最大的变量类型是int[5],所以占用20个字节,它的大小是20,而由于 union中 double占了8个字节,因此 union是要8个字节对齐,所占内存空间为8的倍数。为了实现8个字节对齐,所占空间为24。而data是一个结构体变量,每个变量分开占用空间,依次为 sizeof(int) + sizeof(DATE) + sizeof(double) = 4 + 24 + 8 = 36,按照8字节对齐,占用空间为40,所以结果为40+24=64。
左值和右值是什么?
左值是指可以出现在等号左边的变量或表达式,它最重要的特点就是可写(可寻址)。也就是说,它的值可以被修改,如果一个变量或表达式的值不能被修改,那么它就不能作为左值。
右值是指只可以出现在等号右边的变量或表达式。它最重要的特点是可读。一般的使用场景都是把一个右值赋值给一个左值。
通常,左值可以作为右值,但是右值不一定是左值。
什么是短路求值?
#include <stdio.h>
int main()
{
int i = 6;
int j = 1;
if(i>0||(j++)>0);
printf("%D\\r\\n",j);
return 0;
}
输出结果为1。
输出为什么不是2,而是1呢?这里就涉及一个短路计算的问题。由于 if 语句是个条件判断语句,里面是有两个简单语句进行或运算组合的复合语句,因为或运算中,只要参与或运算的任意表达式的值为真,则整个运算结果为真。而由于变量i的值为6,已经大于0了,而该语句已经为true,则不需要执行后续的 j++ 操作来判断真假,所以后续的 j++ 操作不需要执行,j 的值仍然为1。
因为短路计算的问题,对于&&操作,由于在两个表达式的返回值中,如果有一个为假则整个表达式的值都为假。如果前一个语句的返回值为 false,则无论后一个语句的返回值是真是假,整个条件判断都为假,不用执行后一个语句。
++a和a++有什么区别?两者是如何实现的?
a++的具体运算过程为:
int temp = a;
a = a + 1;
return temp;
++a的具体运算过程为:
a = a + 1;
return a;
后置自增运算符需要把原来变量的值复制到一个临时的存储空间,等运算结束后才会返回这个临时变量的值。所以前置自增运算符效率比后置自增要高。
网硕互联帮助中心![ida远程动调Linux程序学习记录--例题:[hnctf 2022 week2]try2bebug_plus-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/02/20260222112722-699ae81ab7221-220x150.png)



评论前必须登录!
注册