前言
C语言是由Dennis Ritchie与1972年在贝尔实验室开发出来的。Unix操作系统、C编译器和绝大部分的unix应用程序都是C语言编写的。C语言的代码运行速度和汇编语言几乎一样。
1. Basic structure
- 预处理指令(包含指定的都文件),
#include <stdio.h>
- 主函数
int main(){}
,函数从这里开始执行
2. Compiling
gcc -o hello.out hello.c
(默认生产a.out文件)
3. Tokens
c语言就是由一系列的令牌组成的:
- 分号: 每条语句必须以分号结束,跟perl一样
- 注释符: //单行注释;/* 单行或多行注释 */
- 标识符: 不允许有@,¥,%,区分大小写
- 关键字: 如break,do,if等
- 空格
4. Variables
4.1. Defile a variable:type variable_list;
告诉编译器在何处创建变量的存储,以及如何创建变量的存储。
4.2 Variable type
4.2.1 Integers
- char 1字节 char c, ch;
- int 2或4字节 int i, j, k;
- short 2字节
- long 4字节
- size_t >16 bit unsigned integer
- unassigned 没有负数
4.2.2 Float
- float 4字节
- double 8字节
- long double 16字节
4.2.3 void
- 函数返回为空:需要在定义函数的时候在函数前加void
- 函数参数为空:如
int rand(void)
- 指针指向void:如内存分配函数
void *malloc(size_t size)
返回指向void的指针
4.2.4 引申类型
4.2.4.1 Array
存储一个相同类型元素的集合:
- 申明数组:type arrayName [arraySize]
- 初始化数组:double a[5] = {0.1, 3.4, 8.0, 1000.0, 5.2}
- 访问数组元素:索引从0开始
4.2.4.2 Enumeration
enum {
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
4.2.4.3 String
实际上是使用null字符’\0’终止的一维字符数组(char是整数类型):
- 创建字符串:
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char greeting[] = "Hello";
printf("Greeting message: %s\n", greeting );
- 字符串操作:
strcpy(s1, s2) 复制字符串 s2 到字符串 s1
strcat(s1, s2) 连接字符串 s2 到字符串 s1 的末尾
strlen(s1) 字符串 s1 的长度
strcmp(s1, s2) s1=s2相同返回0;s1<s2返回小于0;s1>s2 返回大于0
strchr(s1, ch) 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置
strstr(s1, s2) 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置
4.2.4.4. Pointer:
其值为另一个变量的内存位置的直接地址,用*来指定变量为指针,在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个NULL值
- 创建和使用指针:
int main () {
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("Address of var variable: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("Address stored in ip variable: %p\n", ip );
/* 使用指针访问值 */
printf("Value of *ip variable: %d\n", *ip );
return 0;
}
- 函数指针:
type (*fun_ptr)(type_of_args)
int max(int x, int y) {
return x > y ? x : y;
}
int main(void) {
/* p是函数指针 */
int (*p)(int, int) = &max; // &可以省略
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
}
4.2.4.5. Struct:
允许存储不同类型的数据,表示一条记录,如想要跟踪图书馆中的书本属性,如title,author等等
- 定义结构体:
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
int main() {
printf("title : %s %d\n", book.title);
}
4.2.4.6. Union:
在相同的内存位置存储不同的数据类型,共用体可以有多个成员但任何时候只能有一个成员带有值。共用体占用的内存应足够存储共用体中最大的成员。
- 定义共用体
union Data {
int i;
float f;
char str[20];
} data;
int main( ) {
union Data data;
data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming");
return o;
}
4.2.4.7. Bit-fields:
存储布尔型变量,节省内存
- 定义位域:
struct {type [member_name] : width ;}
;
struct {
unsigned int age : 3;
} Age;
int main( ) {
Age.age = 4;
printf( "Age.age : %d\n", Age.age );
return 0;
}
4.3 Variable initialization:
type variable_name = value; 如int i=3, f=5;
5. Constant:
5.1. Define a constant:
#define identifier value
; 一般放在主函数外面,常量名一般都用大写const type variable = value
; 在主函数内部定义
5.2. Type of constant:
5.2.1. Integers:
- 十进制 85
- 八进制(0开头) 0213
- 十六进制(0x或0X开头) 0x4b
- 无符号整数(u结尾) 30u
- 长整数(l结尾) 30l
- 无符号长整数 30ul
5.2.2. Float:
- 浮点类型 12.5f 等等
5.2.3. 字符常量:
\\, \n, \t等等
6. 存储类:
用来定义变量或函数的范围和生命周期
- auto类:所有局部变量(函数内部)默认的存储类,主能在函数内部使用
- register类:定义存储在寄存器中而不是内存中的局部变量,寄存器只用于需要快速访问的变量,比如计数器。
- static类:static的字面意思就是恒定,static定义的变量只初始化一次,在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。
- extern类:字面意思外部,用来在另一个文件中声明一个全局变量(函数外部)或函数。
7. Operator:
- 算术运算符:
+,-,*,/,%,++,—-
- 关系运算符:
>, <, >=, <=, ==, !=
- 逻辑运算符:
&&, ||, !
- 位运算符:
&, |, ^
- 赋值运算符:
=, +=, -=, *=, /=, %=
- 三元操作符:
?:
- \&返回变量的实际内存地址
- *返回一个指针指向变量
8. Logical statement:
8.1. 判断
8.2. Loop:
- 循环类型:
1. for循环:
for (init; condition; increment) {
statements(s);
}
2. while循环:
while (condition) {
statement(s);
}
8.3. Loop control:break,continue,
9. Function:
- 定义申明:告诉编译器函数的名称、返回类型和参数,
return_type function(args)
, 注意如果想将函数的定义放在程序的最后面,那么在前面使用这个函数之前需要先申明它 - 函数定义:加上函数主体,
return_type function(args) {body}
10. In and Out(c treats everything as file, including the hardware):
1. scanf()与printf():
int main() {
float f;
printf("Enter a number: ");
// %f 匹配浮点型数据
scanf("%f",&f);
printf("Value = %f", f);
return 0;
}
2. getchar():从屏幕读取下”一个”可用的字符,并把它返回为一个”整数”,不是字符串;
pitchar():把”一个”字符输出到屏幕上,并返回相同的字符。
int main( ){
int c;
printf( "Enter a value :");
c = getchar( );
printf( "\nYou entered: ");
putchar( c );
printf( "\n");
return 0;
}
3. gets(): 从stdin读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF;
puts(): 把字符串 s 和一个尾随的换行符写入到 stdout
int main( ) {
char str[100];
printf( "Enter a value :");
gets( str );
printf( "\nYou entered: ");
puts( str );
return 0;
}
11. File IO:
11.1 Read in file:
int fputs(const char *s, FILE *fp);
int fprintf(FILE *fp,const char *format, ...)
int main() {
FILE *fp = NULL;
fp = fopen("/tmp/test.txt", "w+");
fprintf(fp, "This is testing for fprintf...\n");
fputs("This is testing for fputs...\n", fp);
fclose(fp);
}
11.2 读取文件:
char *fgets(char *buf, int n, FILE *fp)
; 从流中读取一个字符串, 把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。
int fscanf(FILE *fp, const char *format, …)
; 从文件中读取字符串,但在遇到第一个空格字符时停止读取。
int main() {
FILE *fp = NULL;
char buff[255];
fp = fopen("/tmp/test.txt", "r");
fscanf(fp, "%s", buff);
printf("1: %s\n", buff );
fgets(buff, 255, (FILE*)fp);
printf("2: %s\n", buff );
}
12. Preprocessor:
以#开头,如#define(定义宏), #include(包含头文件), #undef(取消已定义的宏)等等。
预定义的宏:
__DATE__; __TIME__; __FILE__; __LINE__; __STDC__
13. Head file:
拓展名为.h,包含了C函数声明和宏定义,被多个源文件中引用共享。建议把所有的常量、宏、系统全局变量和函数原型写在头文件中
语法:
#include <file> 在系统目录的标准列表中搜索名为 file 的文件
#include "file" 在包含当前文件的目录中搜索名为 file 的文件
14. Error:
在发生错误时,大多数的C或UNIX函数调用返回1或 NULL,同时会设置一个错误代码errno,需要包含errno.h头文件
perror()
函数:显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
strerror()
函数:返回一个指针,指针指向当前 errno 值的文本表示形式。
#include <stdio.h>
#include <errno.h>
#include <string.h>
extern int errno ;
int main () {
FILE * pf;
int errnum;
pf = fopen ("unexist.txt", "rb");
if (pf == NULL) {
errnum = errno;
fprintf(stderr, "错误号: %d\n", errno);
perror("通过 perror 输出错误");
fprintf(stderr, "打开文件错误: %s\n", strerror( errnum ));
} else {
fclose (pf);
}
return 0;
}
15. Memory management:
动态分配和管理的函数在
15.1. 内存管理函数:
void *calloc(int num, int size)
; 在内存中动态地分配num个长度为size的连续空间,并将每一个字节都初始化为0void free(void *address)
; 释放 address 所指向的内存块,释放的是动态分配的内存空间void *malloc(int num)
; 在堆区分配一块指定大小的内存空间,用来存放数据void *realloc(void *address, int newsize)
; 重新分配内存,把内存扩展到newsize
15.2 动态分配内存:
需要定义一个指针,该指针指向未定义所需内存大小的字符,后续再根据需求来分配内存
15.3. 重新调整内存的大小和释放内存:
在不需要内存时,应该调用函数free()来释放内存,或通过调用函数 realloc() 来增加或减少已分配的内存块的大小
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char name[100];
char *description;
strcpy(name, "Zara Ali");
/* 动态分配内存 */
description = (char *)malloc( 200 * sizeof(char) );
if( description == NULL ) {
fprintf(stderr, "Error - unable to allocate required memory\n");
}
else {
strcpy( description, "Zara ali a DPS student in class 10th");
}
/* 假设您想要存储更大的描述信息 */
description = realloc( description, 100 * sizeof(char) );
if( description == NULL ) {
fprintf(stderr, "Error - unable to allocate required memory\n");
}
else {
strcat( description, "She is in class 10th");
}
printf("Name = %s\n", name );
printf("Description: %s\n", description );
/* 使用 free() 函数释放内存 */
free(description);
}