C - 概述
需要markdown文件的,可以后台回复“cpp”获取md文件,文件随着本系列的更新持续更新,届时请重复获取。
C语言概述
C语言的执行速度快,执行效率高,功能强大,编程自由。
但缺点也是有的,代码实现的周期较长,可移植性较差, 由于过于自由,模块的操作单一(历史性原因)。
因为C语言是底层代码,所以需要了解计算机的基本结构。
计算机结构组成
计算机系统结构组成
C语言的应用领域与特点
C语言的应用极其广泛,从网站后台,到底层操作系统,从多媒体应用到大型网络游戏,均可使用C语言来开发。
- C语言可以写网站后台程序。
- C语言可以专门针对某个主题写出功能强大的程序库。
- C语言可以写出大型游戏的引擎。
- C语言可以写出另一个语言来。
- C语言可以写操作系统和驱动程序,并且只能用C语言编写。
- 任何设备只要配置了微处理器,就都支持C语言。从微波炉到手机,都是由C语言技术来推动的。
C语言保留字
C语言仅有32个关键字,9种控制语句,34种运算符。
关键字(32)
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 unsigned union void
volatile while
1. auto:声明自动变量,缺省时编译器一般默认为auto;最宽宏大量的关键字
2. int:声明整型变量
const int MAX_LENGTH =100;//这不是常量,而是一个只读变量。`
3. double: 声明双精度变量
4. long:声明长整型变量
5. char:声明字符变量
单纯的char类型应该用于字符值的存储使用,唯一允许的操作就是赋值和相同运算符(=,==,!=),signed char和unsigned char型变量只能用于数值的存储与使用。
signed char 范围:-128 ~ 127 unsigned char 范围:0 ~ 255
6. float:声明浮点型变量
7. short:声明短整形变量
不同类型之间的运算要注意精度扩展问题,一般低精度数据要向高精度数据扩展。
8. signed:声明有符号类型变量
9. unsigned:声明无符号类型变量
int main(){
signed char a[1000];
int i;
for(i=0;i<1000;i++)
{
a[i] = -1-i;
}
printf('%d",strlrn(a));
return 0;
}此时输出应该为255。解析:signed char类型表示的值的范围是[-128 ,127],有符号数在内存中存储时先转换为二进制,对每一位取反(符号位不变),然后加1,当i=127时,a[127]的值为-128,当i继续增加,a[128]的值不能是-129,因为这时会发生溢出,因此最高位丢弃,剩下的8位是原来9位补码的低8位的值,当i继续增加到255时,-256补码的低8位为0,然后随着i的增加,开始了新一轮的循环。
注意:strlen函数是计算字符串长度的,并不包含字符串最后‘\0’,则认为本字符串结束。
10. struct:声明结构体变量;结构体所占内存大小是其成员所占内存之和。
11. union:声明联合数据类型变量;维护足够的空间来放置多个数据成员的“一种”,而不是为每一个数据成员配置空间,所有的数据成员具有相同的起始地址。一个union只配置一个足够大的空间来容纳最大长度的数据成员。
12. enum:声明枚举类型变量;enum变量类型还可以给其中的常量符号赋值,如果不赋值则会从被赋初值的那个常量开始依次加1,如果都没有赋值,他们的值从0开始依次递增1.
13. static:声明静态变量;最名不符实的关键字
主要作用:
1).修饰变量静态全局变量:作用域从定义之处开始,到文件结尾处结束 静态局部变量:在函数体中定义时,只能在这个函数中使用,即使这个函数运行结束了,这个静态变量的值在函数下次使用时,仍然能用。
2).修饰函数函数前面加static使得函数成为静态函数,此时函数的作用域仅限于本文本,使得不同的人编写不同的函数时,不用担心自己定义的函数是否与其它函数同名。
14. switch:用于开关变量
15. case:用于语句分支
注意:case 后面只能是整型或字符型常量表达式。
16. default:开关语句中的其他分支
注意:每个case语句的结尾绝对不要忘记加break,否则将导致多个分支重叠;最后必须使用default分支,即使程序不需要default处理;switch语句中禁止使用return 语句。
17. break:跳出当前循环;表示终止本层循环
18. continue:结束当前循环,开始下一轮循环。
19. register:声明寄存器变量;最快的关键字
register变量必须是能够被CPU寄存器所接受的类型,也就意味着register变量必须是一个单个的值,并且长度一个小于或者等于整型长度,而且不能用&来获取register变量的地址,因为此变量可能不存放在内存中。
20. const:声明只读变量,其修饰的只读变量必须在定义的同时初始化。
const int * p;//p可变,*p也就是p指向的对象不可变
int const * p;//p可变,p指向的对象不可变
int * const p;//p不可变,p指向的对象可变
const int * const p;//指针p和p指向的对象都不可变21. volatile:说明变量在出现执行中可被隐含地改变 ;最易变的关键字
volatile 是一种类型修饰符,用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统,硬件或者其他线程等。
22. typedef:用以给数据类型取名
typedef int int32;
unsigned int32 j =10;编译时会出错
#define INT32 int;
unsigned INT32 i =10;编译时并没有报错,因为在预编译的时候INT32被替换为int,语句unsigned int i=10;是正确的。
23. extern:声明变量是在其他文件中声明;最会带帽子的关键字
24. return:子程序返回语句,用来终止一个函数并返回后面跟着的值
注意:return 语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束时被自动销毁。
25. void:声明函数无返回值或者无参数,声明空类型指针
** 作用**:对函数返回值的限定,对函数参数的限定。众所周知,如果指针p1和p2的类型相同,我们可以直接在p1和p2之间互相赋值;如果p1和p2指向不同类型的数据 ,则必须进行强制类型转换运算符,**把赋值运算符右边指针的类型转换为左边指针的类型。**而void *则不同,任何类型的指针都可以直接复制给它,无需进行强制类型转换。
float *p1;
int *p2;
p1=(float * p2);//如果直接写为p1 = p2编译会出错
void *p1;
int *p2;
p1 = p2;//“空类型”可以包容“有类型”
void *p1;
int *p2;
p2 = p1;//“有类型”不能包括“空类型”,编译时会报错。26. do:循环语句的循环体
27. while:循环语句的循环条件
28. for:一种循环语句
1).while 循环:先判断while 后面括号里面的值,如果为真则执行其后面的代码,否则不执行。while(1) 表示死循环,要注意它的使用。
2). do -while循环:先执行do后面的代码,然后判断while后面括号里的值,如果为真,循环开始,否则,循环不开始。
3).for循环:可以很容易控制循环次数,多用于事先知道循环次数的情况下。
29. if:条件语句
30. else:条件语句否定分支
31. goto:无条件跳转语句
goto语句可能跳过了变量的初始化,重要的计算等语句,因此提倡禁用goto语句。
32. sizeof:计算对象所占内存空间大小;最冤枉的关键字。
例如:32位系统
int *p =NULL;
sizeof(p)=4 sizeof (*p)=4
int a[100];
sizeof(a)=4 sizeof(a[100])=400 sizeof(&a)=4 sizeof(&a[0])=4
int b[100];
void fun(int b[100]){
sizeof(b);
}此时sizeof(b)=4
(2)定义与声明
两者最重要的区别:定义创建了对象并为这个对象分配了内存;声明没有分配内存。
(3)循环语句的注意点
1.在多重循环中,尽量将最长的循环放在最内存,以减少CPU跨切循环层的次数
2.for语句的循环控制变量取值采用“半开半闭区间”
3.不能在for循环中修改循环变量,防止循环失控
4.循环尽可能短,解决办法:第一,重新设计这个循环,确认是否这些操纵都必须放在这个循环里面,第二,将这些代码改写成一个子循环,循环中调用这个子函数即可。
5.for语句控制表达式不能包括任何浮点类型的对象。
(4)void 指针
1.小心使用void指针类型
在实际的程序设计中,为了提高程序的可移植性,我们可如下编写代码:
void *pvoid;
(char *)pvoid ++;
(char *)pvoid +=1;
1232.如果函数的参数可以是任意类型指针,那么应该声明其参数为 void *
典型的,例如:内存操作函数memcpy 和memset 的函数原型分别为:
void *memcpy(void * dest,const void *src,size_t len);
void *memset(void *buffer,int c,size_y num);
12(4)大小端模式对union类型数据的影响大端模式:字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。小端模式:字数据的高字节存储在高地址中,而字数据的低地址则存放在低地址中。union型数据所占的空间等于其最大的成员所占的空间,对union型成员的存取都从相对于该联合体基地址的偏移量为0处开始,也就是联合体的访问不论对哪个变量的存取都是从union的首地址位置开始的。
控制语句(9)
if()~else 条件语句
for()~ 循环语句
while()~ 循环语句
do~while() 循环语句
continue 结束本次循环语句
break 中止执行switch或循环语句
switch 多分支选择语句
goto 转向语句
return 从函数返回语句
当 break 关键字用于 while、for 循环时,会终止循环而执行整个循环语句后面的代码。break 关键字通常和 if 语句一起使用,即满足条件时便跳出循环。
使用 while 循环计算1加到100的值:
#include
int main(){
int i=1, sum=0;
while(1){ //循环条件为死循环
sum+=i;
i++;
if(i>100) break;
}
printf("%d\n", sum);
return 0;
}
在多层循环中,一个 break 语句只向外跳一层。例如,输出一个4*4的整数矩阵:
#include
int main(){
int i=1, j;
while(1){ // 外层循环
j=1;
while(1){ // 内层循环
printf("%-4d", i*j);
j++;
if(j>4) break; //跳出内层循环
}
printf("\n");
i++;
if(i>4) break; // 跳出外层循环
}
return 0;
}
continue 语句的作用是跳过循环体中剩余的语句而强制进入下一次循环。continue语句只用在 while、for 循环中,常与 if 条件语句一起使用,判断条件是否成立。
#include
int main(){
char c = 0;
while(c!='\n'){ //回车键结束循环
c=getchar();
if(c=='4' || c=='5'){ //按下的是数字键4或5c
continue; //跳过当次循环,进入下次循环
}
putchar(c);
}
return 0;
}
程序遇到while时,变量c的值为
'\0'
,循环条件c!='\n'
成立,开始第一次循环。getchar() 使程序暂停执行,等待用户输入,直到用户按下回车键才开始读取字符。
运算符(34)
算数运算符:
+ - * / % ++ --
关系运算符:
>= !=
逻辑运算符:
! && ||
位运算符:
<> ~ | ^ &
赋值运算符:
=
条件运算符:
?:
逗号运算符:
,
指针运算符:
* &
其他运算符:
sizeof (类型) -> [] ()
hello world
# include
int main(void){
printf("hello world!\n");
return 0;
}
**# **头文件关键字,表示引入头文件
include引入头文件关键字
**stdio.h **系统标准的输入输出库
<> 表示系统库函数 使用双引号则是用户自定义库函数
int main(void)
int 表示函数返回的整数类型:mian函数关键字,必须有,且只有一个,表示程序的入口, void表示调用无需含参
**{}**表示函数体的起始位置与结束位置
printf打印出内容
return 返回当前函数调用--》退出程序。0要跟main函数的返回值一一对应。返回0表示正常结束
解决提示窗一闪而过的方法:
1、通过**system("pause")**函数解决
# include
int main(void){
printf("hello world!\n");
system("pause"); //暂停一下
return 0;
}2、借助vs,devcpp 等c编程工具解决。
在项目属性中配置属性链接器中选择:“控制台(/SUBSYSTEM :CONSOLE)”
Gcc编译处理步骤
从 hello.c 到 hello(或 a.out)文件, 必须历经 hello.i、 hello.s、 hello.o,最后才得到 hello(或a.out)文件,分别对应着预处理、编译、汇编和链接 4 个步骤
(1) 预处理, C 编译器对各种预处理命令进行处理,包括头文件包含、宏定义的扩展、条件编译的选择等;(2) 编译,将预处理得到的源代码文件,进行“翻译转换”,产生出机器语言的目标程序,得到机器语言的汇编文件;(3) 汇编,将汇编代码翻译成了机器码,但是还不可以运行;(4) 链接,处理可重定位文件,把各种符号引用和符号定义转换成为可执行文件中的合适信息,通常是虚拟地址。
预处理
gcc -E xx.c -O xx. i
在 gcc 命令加上-E 参数,可以得到预处理文件。输入下列命令:vmuser@Linux-host:hello$ gcc -E hello.c –o hello.i 将会产生 hello.i 文件,这就是 hello.c 经过预处理后的文件
编译 在 gcc 编译参数加上-S,可以将 hello.i 编译成 hello.s 文件。命令如下:
gcc -S hello.i
汇编 得到了汇编文件后,通过 gcc 就可以得到机器码了。在终端输入下列命令,可以得到hello.o 文件。
gcc -c hello.s
链接 尽管已经得到了机器码,但这个文件却还是不可以运行的,必须要经过链接才能运行。在终端输入下列命令,将会得到可执行文件 a.out。
gcc hello.o
链接可分为动态链接和静态链接:
vmuser@Linux-host:hello$ gcc hello.o -static -o hello_static
- 动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态库才能运行。动态链接生成的程序小巧,但是必须依赖动态库,否则无法执行。
- Linux 下的动态链接库实际是共享目标文件(shared object),一般是.so 文件,作用类似于 Windows 下的.dll 文件。
- 静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过体积较大。
- Linux 下静态库是汇编产生的.o 文件的集合,一般以.a 文件形式出现。gcc 默认是动态链接,加上-static 参数则采用静态链接。再来看 hello.c 示例,在链接的时候加上-static 参数:
往期回顾
04
|c++ 语法4、 数组
01 |C++语法1、类与对象 |
02 |c++语法2、c执行-命名空间-输入输出 |
03 |c++ 语法3、 数据类型 |
图 | 郭嘉
文 | 郭嘉
![30b2e0902578a183a40611ee9f58202b.png](https://i-blog.csdnimg.cn/blog_migrate/3183883596f4f57bf1d0d5f41b844fb7.jpeg)
扫码关注桔梗与少年
微信号 : if-u-remembers
新浪微博:桔梗与少年