C语言总结——关键字和预处理

一.关键字

static

1. static局部变量和普通局部变量有什么区别?

在函数体中,一个被声明为静态的变量在这一函数被调用过程中维持其值不变, 即:static局部变量位于静态存储区,只被初始化一次,下一次依据上一次结果值。

而普通局部变量位于栈区,函数调用完成后该内存区就被释放,下次调用又要重新初始化。
2. static全局变量与普通的全局变量有异同?

在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所有函数访问,但不能被模块外其它函数访问,它是一个本地的全局变量。 例如:在testa.c中定义了一个全局静态变量static int par=1;, 在testb.c中引用该变量extern int par;,编译就会报错。static限制了变量的作用域。

如果在testa.c中定义了一个全局变量int par=1;, 在testb.c中引用该变量extern int par;,一切都是OK的。

全局变量和static修饰的全局变量都位于静态存储区,都只初始化一次。
3. static函数与普通函数有什么区别?

在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用,其他文件中的函数调用该函数就会报错。

const

1. 关键字const的作用,告诉别人是只读的常量。

2. 通过给优化器一些附加的信息,便于进行类型检查,使用关键字const也许能产生更紧凑的代码。 

3. 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改,这样可以减少bug的出现。

void f(const int i){ i=10;//error! }
4.const 同宏定义一样,可以做到不变则已,一变都变。但是const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

const int a=110; 
const修饰的全局变量在.rodata只读数据段(const变量在定义时必须初始化,所以没有所谓的未初始化const变量),只读数据段在和.text同一个Segment.
const int a;
int const a;    //前两个的作用是一样,a是一个常整型数。
const int *a;   //a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以
int * const a;  //a是一个指向整型数的常指针 (也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)
int const * a const;  //a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)
volatile

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 
1. 并行设备的硬件寄存器(如:状态寄存器) 
2. 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 
3. 多线程应用中被几个任务共享的变量 

补充:

1.一个参数既可以是const还可以是volatile,一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2.一个指针可以是volatile ,尽管这并不很常见。一个例子是,当一个中服务子程序修该一个指向一个buffer的指针时。

sizeof

sizeof不是函数,是C语言中的关键字。

1.例:int par;

sizeof使用形式: sizeof(type),是数据类型必须用括号括住: sizeof(int)

如果不是数据类型,如果是变量可以不用括号括住,如sizeof (par),sizeof par等都是正确形式。

注意:sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。      
sizeof(max)        --若此时变量max定义为int max();
sizeof(char_v)    --若此时char_v定义为char char_v[MAX]且MAX未知,
sizeof(void)      以上都是不正确形式。
2.当操作数是指针时,sizeof依赖于编译器。例:在32位的Ubuntu上测试

void UpperCase( char str[] ) // 将 str 中的小写字母转换成大写字母
{
    for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )//这里的使用sizeof得到的数组大小只有4,数组传参会退化为指针
    if( 'a'<=str[i] && str[i]<='z' )
        str[i] -= ('a'-'A' );
}
3.当操作数具有数组类型时,其结果是数组的总字节数。
char a[5]; int  b[5];
sizeof(a) = 5;sizeof(b) = 20;
4.当操作数是具体的字符串或者数值时,会根据具体的类型进行相应转化。
sizeof(8)    = 4;  //自动转化为int类型
sizeof(8.8)  = 8;  //自动转化为double类型,注意,不是float类型
sizeof("ab") = 3   //自动转化为数组类型,
5.当操作数是联合类型时,sizeof是其最大字节成员的字节数。
union  u{ //对union来说
     char c;
     double d;
}u;
sizeof(u) = max(sizeof(c),sizeof(d)) = sizeof(1,8) = 8;
6.当操作数是结构类型时,sizeof是其成员类型的总字节数,包括补充字节在内。
struct a{             //对struct来说
     char b; 
     double x;
}a;   
Linux上, sizeof(a) = 12;
而一般sizeof(char) + sizeof(double) = 9; 
这是因为编译器在考虑对齐问题时,在结构中插入空位以控制各成员对象的地址对齐。但如果全对齐的话,sizeof(a) = 16, 这是因为b被放到偏移量为0的地址,占1个字节;在存放x时,double类型长度为8,需要放到能被8整除的偏移量上,这时候需要补7个空字节, 达到8个,这时候偏移量为8,放上x后长度为16。在此例中,所有的结构成员都要放在被4整除的地址(Linux的存放方式),这里补3个字节,所以为12。
7.当操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小,Linux中值为4。
8.sizeof与其他操作符的关系。sizeof的优先级为2级,比/、%等3级运算符优先级高,它可以与其他操作符一起组成表达式。
int i = 10; i*sizeof(int);
typeof

typeof关键字是C语言中的一个新扩展,typeof的参数可以是两种形式:表达式或类型,typeof()。这类似于sizeof关键字接受的操作数(与sizeof不同的是,位字段允许作为typeof实参,并被解释为相应的整数类型)。从语义上看,typeof 关键字将用做类型名(typedef名称)并指定类型。

1.使用typeof的声明
下面是两个等效声明,用于声明int类型的变量a。

typeof(int) a; /* Specifies variable a which is of the type int */ 
typeof('b') a; /* The same. typeof argument is an expression consisting of character constant which has the type int */
以下示例用于声明指针和数组。为了进行对比,还给出了不带typeof的等效声明。
typeof(int *) p1, p2; /* Declares two int pointers p1, p2 */
int *p1, *p2;

typeof(int) * p3, p4;/* Declares int pointer p3 and int p4 */
int * p3, p4;

typeof(int [10]) a1, a2;/* Declares two arrays of integers */
int a1[10], a2[10];
如果将typeof用于表达式,则该表达式不会执行。只会得到该表达式的类型。以下示例声明了int类型的var变量,因为表达式foo()是int类型的。由于表达式不会被执行,所以不会调用foo函数。
extern int foo();
typeof(foo()) var;
2.使用typeof的声明限制
请注意,typeof构造中的类型名不能包含存储类说明符,如extern或static。不过允许包含类型限定符,如
const或volatile。例如,下列代码是无效的,因为它在typeof构造中声明了extern,
typeof(extern int) a;
下列代码使用外部链接来声明标识符b是有效的,表示一个int类型的对象。下一个声明也是有效的,它声明了一个使用const限定符的char类型指针,表示指针p不能被修改。
extern typeof(int) b;
typeof(char * const) p = "a";
3.在宏声明中使用typeof
typeof构造的主要应用是用在宏定义中。可以使用typeof关键字来引用宏参数的类型。

二.预处理

预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。

指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码,相当于#elseif
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息

1.用来预防多重包含同一头文件,一般用在头文件中

#ifndef   __TEST_H__
#define  __TEST_H__
头文件的正文内容
#endif
#ifndef指示检测预__TEST_H__处理器变量是否未定义,如果未定义,那么后面所有的指示全被处理直到出现#endif

2.一种或者两种情况的条件编译

#define TEST  //自己决定是否需要定义该宏
#ifdef TEST
程序段1 
#else 
程序段2 
#endif 
或者

#ifdef 0
程序段1 
#else 
程序段2 
#endif 
它的作用是:当TEST已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。  其中#else部分也可以没有,即:  

#ifdef TEST
程序段1 
#endif 
用该条件编译也可用来做注释

#ifdef 0
程序段1 
#endif 
3.多个条件的条件编译
#define CHOICE 2
#if 1 == CHOICE
程序段1
#elif 2 == CHOICE
程序段2
#elif 3 == CHOICE
程序段3
#endif

#define DeBug1
#ifdef DeBug1
程序段1
#elif defined DeBug2
程序段2
#elif defined DeBug3
程序段3
#endif
4.#运算符

出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。例如:
#define PASTE(n) "adhfkj"#n
void main()
{
    printf("%s\n",PASTE(15));
}
宏定义中的#运算符告诉预处理程序,把源代码中任何传递给该宏的参数转换成一个字符串。所以输出应该是adhfkj15。
4.##运算符
##运算符用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符号。看下面的例子:
#define NUM(a,b,c) a##b##c
#define STR(a,b,c) a##b##c
void main()
{
    printf("%d\n",NUM(1,2,3));
    printf("%s\n",STR("aa","bb","cc"));
}
最后程序的输出为:

123
aabbcc

5.#error用于生成一个编译错误消息,并停止编译并停止编译
用法 #error message
注:message不需要用双引号包围
#error编译指示字用于自定义程序员特有的编译错误消息
类似的,#warning用于生成编译警告,但不会停止编译

#include <stdio.h>
//方便在编译的时候打印出正在编译的什么 工程中会编译很久,方便马上停止
#if defined(ANDROID20)
    #pragma message("Compile Android SDK 2.0...")
    #define VERSION "Android 2.0"
#elif defined(ANDROID23)
    #pragma message("Compile Android SDK 2.3...")//以附注的形式打印出来
    #define VERSION "Android 2.3"
#elif defined(ANDROID40)
    #pragma message("Compile Android SDK 4.0...")
    #define VERSION "Android 4.0"
#else
    #error Compile Version is not provided!
#endif
//gcc -DANDROID23 test.c -o test 命令窗口
int main()
{
    printf("%s\n", VERSION);//宏替换
    return 0;
}

6.#line用于强制指定新的行号和编译文件名,,并对源程序 并对源程序的代码重新编号
#line number filename
注:filename可省略
#line编译指示字的本质是重定义__LINE__和__FILE__
编译器是否遵循标准C规范 __STDC__

#include <stdio.h>




#line 1 "Hello.c"//从此行开始编号为7,重命名为Hello.c

#define CONST_NAME1 "CONST_NAME1"
#define CONST_NAME2 "CONST_NAME2"

void f()
{
    return;
}

int main()
{
    printf("%s\n", CONST_NAME1);
    printf("%s\n", CONST_NAME2);
    printf("%d\n", __LINE__);
    printf("%s\n", __FILE__);
    
    f();

    return 0;
}


17:01:01 编译时的时间 __TIME__
Jan 31 2012 编译时的日期 __DATE__
25 当前行号 __LINE__
file1.c 被编译的文件名 __FILE__
7.# pragma是编译器指示字,,用于指示编译器完成一些特定的动作
# pragma所定义的很多指示字是编译器和操作系统特有的
# pragma在不同的编译器间是不可移植的
预处理器将忽略它不认识的# pragma指令
两个不同的编译器可能以两种不同的方式解释同一条# pragma指令
一般用法:#pragma parameter
注:不同的parameter参数语法和意义各不相同
#pragma pack//用法#pragma pack(2)
CPU对内存的读取不是连续的,而是分成块读取的,块的大小只 块的大小只能是1、2、4、8、16字节
当读取操作的数据未对齐,则需要两次总线周期来访问内存,因此性能会大打折扣
某些硬件平台只能从规定的地址处取某些特定类型的数据,否则抛出硬件异常

8.#define,定义宏

用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 
我在这想看到几件事情:
1). #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
2). 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
4). 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要
写一个“标准”宏,这个宏输入两个参数并返回较小的一个。
#define Min(X, Y) ((X)>(Y)?(Y):(X)) //宏中小心地把参数用括号括起来
#define f     (x)     ((x)-1)//直接报错,X未定义  宏函数之间不能有空格
不用中间变量,实现两变量的交换
 

#define swap(x,y) \
x=x+y;\
y=x-y;\
x=x-y //注意:结尾没有分号

#define swap(a,b) \
a = a ^ b;\
b = a ^ b;\
a = a ^ b
inline 和define 对比,inline代码放入预编译器符号表中,高效;它是个真正的函数,调用时有严格的参数检测
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要遍历C语言中的关键字,可以使用C语言标准库中的<string.h>头文件和<ctype.h>头文件中的函数。下面是一种实现方式: 1. 定义一个字符数组来存储C语言的所有关键字,例如: ``` char *keywords[] = {"auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"}; ``` 2. 对于输入的每个单词,使用C语言标准库中的<ctype.h>头文件中的函数检查它是否为关键字。例如,使用strcmp函数比较输入的单词是否与数组中的任何一个关键字相等: ``` int is_keyword(char *word) { int i, n; n = sizeof(keywords) / sizeof(char *); for (i = 0; i < n; i++) { if (strcmp(word, keywords[i]) == 0) { return 1; } } return 0; } ``` 3. 对于输入的每个单词,还需要使用C语言标准库中的<ctype.h>头文件中的函数将其转换为小写字母,以便与关键字数组中的字符串进行比较。例如,使用tolower函数将单词转换为小写字母: ``` void to_lower(char *word) { while (*word) { *word = tolower(*word); word++; } } ``` 4. 最后,可以将输入的文本分解成单词,并调用is_keyword函数和to_lower函数来检查它们是否为关键字。 以下是一个完整的示例程序,演示如何遍历C语言中的关键字: ``` #include <stdio.h> #include <string.h> #include <ctype.h> char *keywords[] = {"auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"}; int is_keyword(char *word) { int i, n; n = sizeof(keywords) / sizeof(char *); for (i = 0; i < n; i++) { if (strcmp(word, keywords[i]) == 0) { return 1; } } return 0; } void to_lower(char *word) { while (*word) { *word = tolower(*word); word++; } } int main() { char text[] = "for (int i = 0; i < n; i++) { printf(\"%d\\n\", i); }"; char *word; word = strtok(text, " "); ### 回答2: 在C语言中,关键字是预定义的标识符,用于表示语言中的特殊功能和结构。要遍历C语言中的关键字,可以按照以下步骤进行: 1. 创建包含C语言所有关键字的数组。根据C语言的标准,可以将关键字存储在一个字符串数组中。 2. 使用循环结构,遍历数组中的每个关键字。可以使用for循环或while循环来实现遍历。 3. 在循环中,将每个关键字打印出来或进行其他操作。可以使用printf函数将关键字输出到控制台上,也可以将其存储在新的数组或其他数据结构中。 4. 执行完循环后,即可遍历所有的C语言关键字。 下面是一个简单的示例代码,用于遍历C语言中的关键字: ```c #include<stdio.h> #include<string.h> int main() { char* keywords[] = {"auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"}; int numKeywords = sizeof(keywords) / sizeof(keywords[0]); for(int i = 0; i < numKeywords; i++) { printf("%s\n", keywords[i]); } return 0; } ``` 以上代码通过使用循环遍历并打印出C语言中的所有关键字。运行代码后,会依次打印出每个关键字,完成关键字的遍历。 ### 回答3: 要遍历C语言中的关键字,首先需要了解C语言关键字有哪些。C语言中的关键字是由编译器预先定义的,用于表示特定功能或命令的保留字。 C语言中的关键字共有32个,包括基本数据类型(如int、float、char等)、流程控制语句(如if、while、for等)、函数定义关键字(如void、return等)等。 遍历C语言中的关键字可以采用以下步骤: 1. 定义一个字符串数组,用于存储C语言中的关键字。 2. 创建一个循环,循环变量从0到31(因为C语言中一共有32个关键字)。 3. 在循环中,依次将每个关键字赋值给字符串数组中的元素。 4. 遍历完所有关键字后,输出或处理字符串数组中的关键字。 以下是示例代码: ``` #include <stdio.h> #include <string.h> int main() { char keywords[32][10] = {"auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"}; int i; for (i = 0; i < 32; i++) { printf("%s\n", keywords[i]); } return 0; } ``` 以上代码会将C语言中的关键字遍历并逐个输出。可以根据实际需求,将关键字存储到其他数据结构中,或进行其他处理操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值