语法
#define
char
不管是32位还是64位系统, char都是一个字节, 对于用 *char 表示的字符串, 如果将每个char用int表示, 其值范围是 -128 ~ 127. 可以试试下面代码打印的结果
char *c = "11月7日召开的“企业合并重组宣布会”上,北京市下属的北京与北京总集团时期0123abcABC12+_=/.,{}~"; while (*c != ' ') { int i = *c; char j = *c; printf("%08x, %4d, %10u, %8c, %4d, %10hhu, %8c ", c, i, i, i, j, j, j); c++; }
typedef
Typedef is a keyword that is used to give a new symbolic name for the existing name in a C program. This is same like defining alias for the commands.
例如
typedef long long int LLI; printf("Storage size for long long int data type : %ld ", sizeof(LLI));
对于下面这样一个struct
struct student { int mark [2]; char name [10]; }
如果用这个struct来声明变量, 有两种方式
// 方式一 struct student record; /* for normal variable */ struct student *record; /* for pointer variable */ // 方式二 typedef struct student status; status record1; /* record 1 is structure variable */
对于第二种方式, 可以合并写成
typedef struct student { int mark [2]; char name [10]; } status;
.
struct
struct student { int mark; char name[10]; float average; }; // 声明变量及其取值方法 struct student report; struct student report = {100, “Mani”, 99.5}; report.mark; report.name; report.average; // 声明变量指针及取值方法 struct student *report, rep; struct student rep = {100, “Mani”, 99.5}; report = &rep; report -> mark; report -> name; report -> average;
.使用struct时需要注意内存对齐方式造成的空间浪费.
Architecture of a computer processor is such a way that it can read 1 word (4 byte in 32 bit processor) from memory at a time.
To make use of this advantage of processor, data are always aligned as 4 bytes package which leads to insert empty addresses between other member’s address.
typedef struct s2 { char c; char cc; double d; } s22; typedef struct s3 { char c; double d; char cc; } s33;
sizeof(s22)是10, 但是系统按照sizeof(double)*2分配: 先分配8个字节给c, c占用1个字节, cc再占1个字节, 剩余6字节不够存储d, 系统就额外为d分配8字节, 整个分配浪费内存6字节;
sizeof(s33)是10, 但是系统按照sizeof(double)*3分配: 先分配8个字节给c, c占用1个字节, 剩余7个字节不够存储d, 所以系统又分配了8个字节给d, 之后又分配8个自己给cc, 总共浪费了14个字节的空间. 所以在定义struct的时候按照s22的顺序分配可以节省内存.
union
联合union在许多其他语言中称作变体记录variant record, 在union中, 所有的成员都从偏移地址零开始存储, 这样每个成员的位置都会重叠在一起. 按照这种特性, union可以把同一个数据解释成多个不同的变量, 该用法的例子
// 可以提取整个32位int, 也可以提取单独的char如 value.byte.c0 union bits32_tag { int whole; //一个32位的值 struct { char c0, c1, c2, c3;} byte; //四个八位的字节 } value;
union与struct的区别: 在存储多个成员信息时, 编译器会给struct每个成员分配存储空间, struct 可以存储多个成员信息, 而union每个成员用同一个存储空间, 存储成员的信息时, 会覆盖其他成员的内容.
volatile
编译器优化常用的方法有: 1) 将内存变量缓存到寄存器, 因为访问寄存器要比访问内存单元快的多; 2) 调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令. 将内存变量缓存到寄存器可能会导致多线程程序读取脏数据, 而使用volatile声明变量值后, 系统会总是从变量所在内存地址读取数据, 遇到volatile变量, 编译器对访问该变量的代码就不再进行优化, 读后的操作不提前, 写前的操作不推后. 使用volatile变量的场景
1. 中断服务程序中修改的供其它程序检测的变量需要加volatile
static int i=0; int main(void) { ... while (1){ if (i) dosomething(); } } /* Interrupt service routine. */ void ISR_2(void) { i=1; }
2. 多任务环境下各任务间共享的标志应该加volatile
3. 存储器映射的硬件寄存器通常也要加voliate,因为每次对它的读写都可能有不同意义
int *output = (unsigned int *)0xff800000;//定义一个IO端口; int init(void) { int i; for(i=0;i< 10;i++){ *output = i; } } # 第一行应该改为 volatile int *output=(volatile unsigned int *)0xff800000;//定义一个I/O端口
Array
多维数组的赋值可以使用一维列表, 例如
// declaring and Initializing array int arr[2][2] = {10,20,30,40}; /* Above array can be initialized as below also arr[0][0] = 10; // Initializing array arr[0][1] = 20; arr[1][0] = 30; arr[1][1] = 40; */
对于变量长度的数组, 声明时内部的值是不可预测的, 如果需要初始化为0, 可以使用memset快速赋值:
int p[length][length]; memset(p, 0, sizeof(p));
String
C语言的字符串就是char数组
//初始化方式 char string[20] = {'f', 'r', 'e', 's', 'h', '2', 'r', 'e', 'f', 'r', 'e', 's', 'h', ' '}; char string[20] = "fresh2refresh"; //会分配20个字节的内存空间 char string[] = "fresh2refresh"; //内存空间会按实际大小分配 char *tmp = "test char point tmp"; // 字符串会分配到常量区 //打印string printf("The string is : %s ", string);
注意上面三个string与下面的tmp的变量类型是不同的, 三个string类型都是char数组, 对应的sizeof(string)都是数组的内存字节数, 分别为20, 20, 13, 而sizeof(tmp)是指针的字节数, 在64位系统中为8. 但是可以使用string给tmp赋值, 赋值后tmp的打印方式也和char数组一样
tmp = string; printf("%s ", tmp);
pointer
指针是一个整形, 在32位系统中占4字节, 在64位中占8字节. 指针一个关键的属性是类型, 即这个指针指向的是什么类型的数据, 指针变量加减运算的结果跟数据类型的字节长度有关.
// 指向char的指针的数组 char *msgTable[5] = {"Sum", "Difference", "Product", "Quotient", "Power"}; // 10个int类型指针的数组 int *arrPtr[10] = NULL; // 指向10个元素的int数组的指针 int (* arrPtr)[10] = NULL; int matrix[3][10]; // 3行10列的数组, 数组名称是一个指向第一个元素的指针, 也就是第一行的指针 arrPtr = matrix; // 使得arrPtr指向矩阵的第一行 (*arrPtr)[0] = 5; // 将5赋值给第一行的第一个元素 arrPtr[2][9] = 6; // 将6赋值给最后一行的最后一个元素 ++arrPtr; // 将指针移动到下一行, 如果将地址打出来, 可以看到地址值增长了40, 正好是10个int的长度 (*arrPtr)[0] = 7; // 将7赋值给第二行的第一个元素
另外还有指向函数的指针, 例如
# p为一个指向函数的指针, 这个函数返回值为int int (*p)(); # 如果有一个函数int foo(),则可以令 p = foo;
对于指针变量声明的阅读, 可以通过"就近原则"来判断. 例如
- int *p - 这里 p和*最近, 所以p首先是一个指针, 这个指针是int类型的
- int *p[5] - 这里 p和[5]最近, 所以p首先是一个数组, 数组的内容呢是指针, 这些指针是int类型的
- int (*p)[5] - 这里 p和*最接近, 所以p首先是一个指针, 这个指针是 int[5] 类型的
- int (*p)() - 这里 p和*最接近, 所以p首先是一个指针, 这个指针是指向 int () 类型的
对于指针传参的理解
指针传递, 对于变量A, 传递A的地址就是指针传递, 这里传递的是A的地址的值, 这个值的载体可以是一个A类型的指针变量, 假设为P, 这个变量P也有自己的地址, 这个地址在主函数和子函数里是不同的, 在子函数里只能操作其携带的地址所指向的值, 修改P的值是没用的, 因为在子函数里P只是一个局部变量. 可以用下面的代码来说明
void point_test(int *p) { printf("Func: point addr:%X, value:%X, target:%d ", &p, p, *p); // Change the target value *p = 128; } int main(void) { int target = 100; int *p = ⌖ printf("Main: point addr:%X, value:%X, target:%d ", &p, p, *p); point_test(p); printf("Main: point addr:%X, value:%X, target:%d ", &p, p, *p); } # 输出 Main: point addr:E5A2A680, value:E5A2A67C, target:100 Func: point addr:E5A2A658, value:E5A2A67C, target:100 Main: point addr:E5A2A680, value:E5A2A67C, target:128
什么时候需要传递"指向指针的指针变量"? 在子函数需要修改"外部指针的值"的时候(注意和"外部指针指向的值"区分), 这种方式经常用于在子函数里申请地址空间的时候, 例如
void point_test(char **p, int num) { printf("Func: point addr:%X, value:%X, target:%X ", &p, p, *p); *p = (char *)malloc(sizeof(char) * num); printf("Func: point addr:%X, value:%X, target:%X ", &p, p, *p); } int main(void) { char *p; printf("Main: point addr:%X, value:%X, target:%X ", &p, p, *p); point_test(&p, 10); printf("Main: point addr:%X, value:%X, target:%X ", &p, p, *p); } # 输出 Main: point addr:F9B4CA0, value:F9B4D90, target:1 Func: point addr:F9B4C78, value:F9B4CA0, target:F9B4D90 Func: point addr:F9B4C78, value:F9B4CA0, target:9C8C8670 Main: point addr:F9B4CA0, value:9C8C8670, target:0
在子函数里新申请了内存空间, 将新内存空间的地址作为值, 写到了F9B4CA0这个地址上. 而F9B4CA0这个地址, 就是main函数里p的地址, 可以看到执行子函数之后, p变了.
内建函数
Type Casting Functions
- atof() Converts string to float
- atoi() Converts string to int
- atol() Converts string to long
- itoa() Converts int to string
- ltoa() Converts long to string
Utils Functions
- getenv() This function gets the current value of the environment variable
- setenv() This function sets the value for environment variable
- putenv() This function modifies the value for environment variable
- perror() Displays most recent error that happened during library function call
- rand() Returns random integer number range from 0 to at least 32767
- delay() Suspends the execution of the program for particular time
Arithmetic Functions
内建的数学函数, 需要包含头文件 math.h 和 stdlib.h
- abs() This function returns the absolute value of an integer. The absolute value of a number is always positive. Only integer values are supported in C.
- floor() This function returns the nearest integer which is less than or equal to the argument passed to this function.
- round() This function returns the nearest integer value of the float/double/long double argument passed to this function. If decimal value is from “.1 to .5”, it returns integer value less than the argument. If decimal value is from “.6 to .9”, it returns the integer value greater than the argument.
- ceil() This function returns nearest integer value which is greater than or equal to the argument passed to this function.
- sin() This function is used to calculate sine value.
- cos() This function is used to calculate cosine.
- cosh() This function is used to calculate hyperbolic cosine.
- exp() This function is used to calculate the exponential “e” to the xth power. 自然数e的n次方
- tan() This function is used to calculate tangent.
- tanh() This function is used to calculate hyperbolic tangent.
- sinh() This function is used to calculate hyperbolic sine.
- log() This function is used to calculates natural logarithm. 自然对数
- log10() This function is used to calculates base 10 logarithm. 以10为底的对数
- sqrt() This function is used to find square root of the argument passed to this function.
- pow(double base, double exponent) This is used to find the power of the given number. 返回base的exponent次方
- trunc() This function truncates the decimal value from floating point value and returns integer value.
Char Validation Functions
内建的字符检查和处理函数, 需要包含头文件夹 ctype.h
- isalpha() checks whether character is alphabetic
- isdigit() checks whether character is digit
- isalnum() Checks whether character is alphanumeric
- isspace() Checks whether character is space
- islower() Checks whether character is lower case
- isupper() Checks whether character is upper case
- isxdigit() Checks whether character is hexadecimal
- iscntrl() Checks whether character is a control character
- isprint() Checks whether character is a printable character
- ispunct() Checks whether character is a punctuation
- isgraph() Checks whether character is a graphical character
- tolower() Checks whether character is alphabetic & converts to lower case
- toupper() Checks whether character is alphabetic & converts to upper case
Time Related Functions
- setdate() This function used to modify the system date
- getdate() This function is used to get the CPU time
- clock() This function is used to get current system time
- time() This function is used to get current system time as structure
- difftime() This function is used to get the difference between two given times
- strftime() This function is used to modify the actual time format
- mktime() This function interprets tm structure as calendar time
- localtime() This function shares the tm structure that contains date and time informations
- gmtime() This function shares the tm structure that contains date and time informations
- ctime() This function is used to return string that contains date and time informations
- asctime() Tm structure contents are interpreted by this function as calendar time. This time is converted into string.
Memory Allocation And Manipulation Functions
malloc()
malloc() is used to allocate space in memory during the execution of the program. It does not initialize the memory allocated during execution. It carries garbage value. It returns null pointer if it couldn’t able to allocate requested amount of memory.
#include <stdio.h> #include <string.h> #include <stdlib.h> int main() { char *mem_allocation; /* memory is allocated dynamically */ mem_allocation = malloc( 20 * sizeof(char) ); if( mem_allocation== NULL ) { printf("Couldn't able to allocate requested memory "); } else { strcpy( mem_allocation,"something"); } printf("Dynamically allocated memory content: %s ", mem_allocation); free(mem_allocation); }
calloc()
Like malloc() but calloc() initializes the allocated memory to zero.
#include <stdio.h> #include <string.h> #include <stdlib.h> int main() { char *mem_allocation; /* memory is allocated dynamically */ mem_allocation = calloc( 20, sizeof(char) ); if( mem_allocation== NULL) { printf("Couldn't able to allocate requested memory "); } else { strcpy( mem_allocation,"fresh2refresh.com"); } printf("Dynamically allocated memory content: %s ", mem_allocation); free(mem_allocation); }
realloc()
realloc() modifies the memory size allocated by malloc() and calloc(). If no enough space in current memory block, new block will be allocated for the full size of reallocation then copies the existing data to new block and then frees the old block.
free()
free() frees the allocated memory by malloc(), calloc(), realloc().
#include <stdio.h> #include <string.h> #include <stdlib.h> int main() { char *mem_allocation; /* memory is allocated dynamically */ mem_allocation = malloc( 20 * sizeof(char) ); if( mem_allocation == NULL ) { printf("Couldn't able to allocate requested memory "); } else { strcpy( mem_allocation,"fresh2refresh.com"); } printf("Dynamically allocated memory content: %s ", mem_allocation); mem_allocation=realloc(mem_allocation,100*sizeof(char)); if( mem_allocation == NULL ) { printf("Couldn't able to allocate requested memory "); } else { strcpy( mem_allocation,"space is extended upto 100 characters"); } printf("Resized memory : %s ", mem_allocation); free(mem_allocation); }
memset()
It is used to initialize a specified number of bytes to null or any other value in the buffer
memcpy(target, string, len)
It is used to copy a specified number of bytes from one memory to another
memmove(str1+2, str1, strlen(str1))
It is used to copy a specified number of bytes from one memory to another or to overlap on same memory. Difference between memmove and memcpy is, overlap can happen on memmove whereas memcpy should be done in non-destructive way
memcmp(str1, str2, 5*sizeof(char))
It is used to compare specified number of characters from two buffers
memicmp()
It is used to compare specified number of characters from two buffers regardless of the case of the characters
memchr(string, 'h', strlen(string))
It is used to locate the first occurrence of the character in the specified string
String Related Functions
string的操作方法通过string.h提供, 包含以下方法
- strcat(str1, str2) 在str1后拼接str2
- strncat(str1, str2, n) 在str1后拼接str2的前n个字符
- strcpy(str1, str2) 将str2 复制到 str1, 如果str1长度不够, 超过部分将被丢弃
- strncpy(str1, str2, n) 将str2的前n个字符复制到str1, 如果str1长度不够, 超过部分将被丢弃
- strlen(str1) 返回str1的长度. 计算到 的位置
- strcmp(str1, str2) str1和str2相等返回0. str1长度小于str2返回负值, str1长度大于str2返回正值, 长度相等, 则逐字比较
- strcmpi() 与strcmp()相似, 但是不区分大小写字符
- strchr(const char *str, int character) str1中第一次出现character的位置, 返回的是char类型指针
- strrchr(const char *str, int character) str1中从末尾开始第一次出现character的位置,
- strstr(const char *str1, const char *str2) str1中第一次出现str2的位置, 返回的是char类型指针
- strrstr(const char *str1, const char *str2) str1从末尾开始, 第一次出现str2的位置, 返回的是char类型指针. 非标准函数, 可能在标准C库中不存在
- strdup(const char *string) 复制字符串, 返回char类型指针, 非标准函数
- strlwr(char *string) 将字符串转为小写, 非标准函数
- strupr(char *string) 将字符串转为大写, 非标准函数
- strrev(char *string) 将字符串倒序, 非标准函数
- strset(char *string, int c) 将字符串所有字符修改为输入的字符, 非标准函数
- strnset(char *string, int c, int n) 将字符串前n个字符修改为输入的字符, 非标准函数
- strtok(char * str, const char * delimiters) 用delimiters里的每个字符作为分隔符, 分隔str字符串. 操作直接在str内存地址中进行. 当strtok()在参数str字符串中发现参数delimiters中包含的字符时, 会将该字符改为 , 在第一次调用时必须给予参数str字符串, 之后的调用则将参数s设置成NULL, 每次调用成功返回指向被分割出片段的指针.
File Related Functions
文件打开模式
- r – 只读模式打开, 返回指针指向第一个字符, 如果文件不存在则返回null
- w – 写模式打开, 文件内容将被覆盖, 如果文件无法打开则返回null
- a – 追加模式打开, 如果文件无法打开则返回null
- r+ – 读写模式打开, 返回指针指向第一个字符. 打开文件不删除内容, 如果文件不存在也不创建
- w+ – 读写模式打开, 返回指针指向第一个字符. 打开文件时删除内容, 如果文件不存在则创建.
- a+ – 读写模式打开, 返回指针指向第一个字符, 但是不能修改已经存在的内容.
文件处理方法
- FILE *fopen (const char *filename, const char *mode) 创建新文件或打开存在的文件
- int fclose(FILE *fp) 关闭打开的文件,
- int getw(FILE *fp) 从文件读取一个整数
- int putw(int number, FILE *fp) 向文件写入一个整数
- int fgetc(FILE *fp) 从文件读取一个字符
- int fputc(int char, FILE *fp) 向文件写入一个字符
- char *gets (char *string) 读取键盘输入的字符串, 内容会被存入string
- int puts(const char *string) 向输出屏幕写字符串
- char *fgets(char *string, int n, FILE *fp) 从文件读取字符串, 每次一行
- int fputs (const char *string, FILE *fp) 向文件写入字符串
- int feof(FILE *fp) 用于判断fp是否已经处于文件结尾
- int fgetchar(void) 读取键盘输入的一个字符
- int fprintf(FILE *fp, const char *format, …) 将字符串格式化后写入文件
- int fscanf(FILE *fp, const char *format, …) 从文件中读入格式化好的内容
- fputchar() fputchar() function writes a character onto the output screen from keyboard input.
- int fseek(FILE *fp, long int offset, int whence) 将文件指针移动到给定的位置
- SEEK_SET 表示文件开始位置, 例如 fseek(fp, 21, SEEK_SET);
- SEEK_CUR 表示文件指针当前位置, 例如 fseek(fp, -10, SEEK_CUR);
- SEEK_END 将文件结尾位置, 例如 fseek(fp, -7, SEEK_END);
- long int ftell(FILE *fp) 得到当前文件指针的位置
- void rewind(FILE *fp) 将文件指针移回文件开始位置
- int getc(FILE *fp) 读取一个字符
- int getch(void) 等待并读取键盘输入的字符, 但是不会在屏幕上显示输入的字符
- int getche(void) 等待并读取键盘输入的字符, 会在屏幕上显示输入的字符
- int getchar(void) 从键盘输入读取一个字符
- int putc(int char, FILE *fp) 显示一个字符到标准输出, 或者写入一个字符到文件, 例如 putc(char, stdout); putc(char, fp);
- int putchar(int char) 朝标准输出或屏幕输出一个字符
- printf() 输出格式化内容
- int sprintf(char *string, const char *format, …) 将变量值输出成格式化字符串
- scanf() 从键盘输入读取格式化内容
- int sscanf(const char *string, const char *format, …) 从格式化字符串中读取变量值
- int remove(const char *filename) 删除文件, 文件名必须包含全路径
- int fflush(FILE *fp) 清空文件或buffer
例子
#include <stdio.h> int main () { FILE *fp; char data[60]; fp = fopen ("test.c","w"); fputs("Fresh2refresh.com is a programming tutorial website", fp); fgets(data, 60, fp); printf("Before fseek - %s", data); // To set file pointet to 21th byte/character in the file fseek(fp, 21, SEEK_SET); fflush(data); fgets(data, 60, fp); printf("After SEEK_SET to 21 - %s", data); // To find backward 10 bytes from current position fseek(fp, -10, SEEK_CUR); fflush(data); fgets(data, 60, fp); printf("After SEEK_CUR to -10 - %s", data); // To find 7th byte before the end of file fseek(fp, -7, SEEK_END); fflush(data); fgets(data, 60, fp); printf("After SEEK_END to -7 - %s", data); // To set file pointer to the beginning of the file fseek(fp, 0, SEEK_SET); // We can use rewind(fp); also fclose(fp); return 0; }