C语言语法
简单示例
# include <stdio.h>
/*
* 注释语句
*/
int main(void) {
int num = 3;
char letter = 'b';
printf("num = %d, letter = %c", num, letter);
return 0;
}
数据类型
整型
char, short int, int, long int, long long int 除了char型,这些整数类型若未指明是signed或unsigned,都表示signed
浮点型
float, double, long double
运算符
位运算
&, |, ^, ~
移位运算
«, »
算术运算符
+, -, *, /, %
赋值运算符
=, +=, *=, /=, %=, «=, »==, &=, |=, ^=
比较运算符
==, !=, >, <, >=, <=
逻辑运算符
&&, ||, !
选择语句
if..else if..else
#include <stdio.h>
int main(void) {
int score = 88;
if (score >= 90) {
printf("great");
} else if (score >= 70) {
printf("good");
} else if (score >= 60) {
printf("pass");
} else {
printf("bad");
}
return 0;
}
good
switch
#include <stdio.h>
int main(void) {
int day = 2;
switch(day) {
case 1:
printf("Monday");
break;
case 2:
printf("Tuesday");
break;
default:
printf("no");
break;
}
}
Tuesday
循环语句
while / do..while
#include <stdio.h>
int main(void) {
int num = 0;
while(num <= 3) {
printf("%d\n", num);
num += 1;
}
return 0;
}
0
1
2
3
#include <stdio.h>
int main(void) {
int num =0;
do {
printf("%d\n", num);
num += 1;
} while(num <=3 );
return 0;
}
0
1
2
3
for
#include <stdio.h>
int main(void) {
for(int i=0; i<=3; i++) {
printf("%d\n", i);
}
printf("-------------\n");
// break
for(int i=0; i<=3; i++) {
if(i==2) break;
printf("%d\n", i);
}
printf("-------------\n");
// continue
for(int i=0; i<=3; i++) {
if(i==2) continue;
printf("%d\n", i);
}
return 0;
}
0
1
2
3
-------------
0
1
-------------
0
1
3
goto
#include <stdio.h>
int main(void) {
printf("1\n");
printf("2\n");
goto error;
printf("3\n");
printf("4\n");
error:
printf("this is error segment\n");
}
1
2
this is error segment
函数
带参数和返回值
#include <stdio.h>
// 若函数定义在被调用之后,要声明
int add_num(int, int);
int main(void) {
int x = 10, y = 20;
printf("%d\n", add_num(x, y));
return 0;
}
int add_num(int num1, int num2) {
return num1 + num2;
}
30
结构体
简单示例
/*
* 结构体定义在函数内
*/
#include <stdio.h>
#include <string.h>
int main(void) {
struct student {
int id;
char name[10];
char gender;
float score;
};
// 声明后赋值
struct student s1;
s1.id = 10;
strcpy(s1.name, "zhubiao");
s1.gender = 'M';
s1.score = 99.9;
printf("%s: %f\n", s1.name, s1.score);
// 声明时赋值
struct student s2 = {11, "zhangya", 'F', 82.5};
printf("%s: %f\n", s2.name, s2.score);
// 定义结构体时就声明变量
struct complex_struct {
double x;
double y;
} z1, z2;
z1.x = 10.3;
z1.y = 15;
printf("%f, %f", z1.x, z1.y);
return 0;
}
zhubiao: 99.900002
zhangya: 82.500000
10.300000, 15.000000
/*
* 结构体定义在函数外
*/
#include <stdio.h>
#include <string.h>
struct student {
int id;
char name[10];
char gender;
float score;
};
int main(void) {
struct student s1 = {1001, "zhubiao", 'M', 99.5};
printf("%s: %f\n", s1.name, s1.score);
return 0;
}
zhubiao: 99.500000
结构体嵌套
#include <stdio.h>
struct t_fs_struct {
// 进程与文件系统的关系
int users;
int umask;
};
struct t_files_struct {
// 打开的文件集
int max_fds;
int max_fdset;
int next_fd;
};
struct t_task_struct {
// 进程描述符
int pid;
int gid;
struct t_fs_struct fs;
struct t_files_struct files;
};
int main(void) {
struct t_task_struct task1 = {
1001,
2001,
{33, 222},
{65535, 65535, 20}
};
printf("umask = %d\n", task1.fs.users);
printf("next_fd = %d\n", task1.files.next_fd);
return 0;
}
umask = 33
next_fd = 20
数组和字符串
数组
数组是一种复合类型,由一些列相同类型的元素组成,和结构体一样各个元素在内存中的存储空间是相邻的,元素可以是基本类型,也可以是复合类型,甚至指针类型,如int, char, struct, int *等。
/*
* 数组元素可以是基本类型,也可以是复合类型
*/
#include <stdio.h>
struct f_task_struct {
int pid;
int gid;
};
int main() {
// 元素是基本类型
int nums[5];
nums[0] = 10;
nums[2] = 20;
// 元素是结构体类型
struct f_task_struct tasks[3];
tasks[0].pid = 1001;
tasks[0].gid = 1002;
tasks[1].pid = 2001;
tasks[1].gid = 2002;
}
/*
* 数组元素地址是相邻的
*/
#include <stdio.h>
int main() {
int nums[5], i;
for(i=0; i<sizeof(nums)/sizeof(nums[0]); i++) {
// 打印各个元素的地址
printf("%d\n", &nums[i]);
}
return 0;
}
1322353632
1322353636
1322353640
1322353644
1322353648
/*
* 数组名存储的是第一个元素的地址
*/
#include <stdio.h>
int main(void) {
int nums[5];
printf("%d: %d", nums, &nums[0]);
return 0;
}
226477552: 226477552
字符串
“hello” 在内存中的存储:
字符串类似存储字符的数组
字符串与字符数组相同之处:
a. 都可以通过下标访问
b. 字符串中的每个字符存储都是相邻的
c. 做右值计算时,自动转换成指向首元素的指针,类似于数组名
字符串与字符数组不同之处:
a. 字符数组是可以改变元素的值,而字符串是只读的。
/*
* 字符串
*/
#include <stdio.h>
int main(void) {
char s1[] = {'h', 'e', 'l', 'l', 'o', '\n'};
// 字符串可以通过下标访问
printf("%c\n", "hello"[0]);
// 做右值计算,自动转换为指向首元素的指针
char s2[] = "hello";
// 字符串默认会在尾部添加一位'\0', 也就是ASCII的NULL字符。
printf("%d\n", sizeof("hello"));
// "hello"[0] = 'a'; 会报错,字符串是只读的
return 0;
}
h
6
/*
* printf("%s");
*/
#include <stdio.h>
int main(void) {
char a[10] = {'h', 'e', '\0', 'l', 'l', 'o'};
char b[] = "good";
char *c;
c = "nice";
// 用printf中,若用%s做占位符,则只要告诉其元素的首地址,printf会一直打印到元素为'\0'为止的元素。
printf("%s\n", a);
printf("%s\n", b);
printf("%s\n", c);
}
he
good
nice
指针
内存单元中保存的若不再是常规的数据,而是指向另外一个内存单元的地址,该内存单元称为指针。
int i;
, 则i
: 为该内存单元中存储的数据,而&i
: 为该内存单元的地址。
指针本质是一个整数,是数组的索引。
简单示例
#include <stdio.h>
int main(void) {
// 指针声明在变量前面写*,
// 除了声明外,赋值等其他地方若变量前有*,则表示取指针存储的地址所指向内存单元中所存的数据。
int *pi;
int num = 10;
pi = #
printf("%d\n", *pi);
*pi = *pi + 10;
printf("%d\n", num);
return
输出
10
20
指针与数组
数组名与指针的关系:
相同之处:
- 在表达式中使用时,数组名相当于一个指针常量,其存储的是数组第一个元素的地址,不可改变其值。
不同之处:
- 使用
sizeof(数组名)
得到的结果是数组所有元素的长度之和,而不是指针的长度。 - 使用&数组名取地址时是指向数组地址的指针。
#include <stdio.h>
int main(void) {
int scores[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *ps, i;
ps=&scores[0];
for(i=0; i<=9; i++) {
printf("%d: %d: %d: %d: %d\n", &ps, ps, &scores[0], &scores[1]);
// ps++所增加的地址是由声明的类型决定的,此处是int, 每次增加4。
ps++;
}
// 数组名存储的值位数组第一个元素的地址。
printf("address of scores: %d", scores);
}
输出
-948465336: -948465328: -948465328: -948465324: 25653296
-948465336: -948465324: -948465328: -948465324: -907652787
-948465336: -948465320: -948465328: -948465324: -907652787
-948465336: -948465316: -948465328: -948465324: -907652787
-948465336: -948465312: -948465328: -948465324: -907652787
-948465336: -948465308: -948465328: -948465324: -907652787
-948465336: -948465304: -948465328: -948465324: -907652787
-948465336: -948465300: -948465328: -948465324: -907652787
-948465336: -948465296: -948465328: -948465324: -907652787
-948465336: -948465292: -948465328: -948465324: -907652787
address of scores: -948465328
指针与const限定符
#include <stdio.h>
int main(void) {
// int const *pa 或 const int *pa声明一个指向const int型的指针,
// pa所指向的地址存储的值不可改变,但指针存储的地址可以改变
int const *pa, a=10;
pa = &a;
pa++;
// *pa = 3;报错
// 字符串在做右值计算的时候,自动转换为指向首元素的指针
char w1[50] = "good";
char *w2 = "nice";
printf("%s", w2);
return 0;
}
输出
nice
函数返回值类型是指针
#include <stdio.h>
// 声明函数的返回的值类型是int指针,传入的参数也是int指针类型。
int *exchange(int *num1, int *num2){
int *tmp = num1;
num1 = num2;
num2 = tmp;
return num1;
}
int main() {
int a, b;
int *result;
a = 10;
b = 20;
result = exchange(&a, &b);
printf("%d", *result);
}
输出
20
指针与结构体
#include <stdio.h>
struct t_files_struct {
int max_fds;
int next_fd;
} f1 = {65536, 20};
struct t_task_struct {
int pid;
int gid;
struct t_files_struct *files;
} task1;
int main(void) {
task1.files = &f1;
// (*task1.files).next_fd 可简写为: task1.files -> next_fd
printf("%d", task1.files -> next_fd);
}
输出
20
指向指针的指针
#include <stdio.h>
int main(void) {
int **p1, *p2, p3;
p3 = 100;
p2 = &p3;
p1 = &p2;
printf("%d: %d: %d\n", p3, *p2, **p1);
printf("%d: %d: %d\n", p1, p2, p3);
}
100: 100: 100
-1676064400: -1676064404: 100
指针数组
数组中可以存储基本类型、也可存储指针类型或者复合类型。当存储的是指针类型就是指针数组
#include <stdio.h>
int main(void) {
int a, b, c;
int *nums[] = {&a, &b, &c};
a = 10;
b = 20;
c = 30;
// 打印指针数组中存储的地址值和该地址指向的内存中的值
printf("%d: %d", nums[0], *nums[0]);
return 0;
}
2145752524: 10
/*
* 指针数组存储的若是字符串的首地址,则用printf(%s)打印将会很有趣
*/
#include <stdio.h>
int main(void) {
// 字符串右值赋值是取首元素的地址。所以才有下面的写法.
char *s[] = {
"hello",
"word",
"nice"
};
// 打印%s只需要告诉其首地址,他就会打印该地址内存储的元素及其后续的元素,直到遇到元素为\0的才停止。
printf("%s\n", s[0]);
printf("%c\n", *s[0]);
return 0;
}
hello
h
函数指针
函数名:
a. 和数组名类似,函数名是指向函数入口的指针
b. sizeof(函数名), &函数名, *函数名:此时函数名是指函数本身。
#include <stdio.h>
void ftest(void) {
int i;
}
int main(void) {
// 函数名是指向函数入口地址的指针
void (*f)();
f = ftest;
// &函数名,*函数名:都是取函数地址
printf("%d: %d: %d", ftest, *ftest, &ftest);
}
1901139621: 1901139621: 1901139621