C语法备注01
(1)char
字符类 char 既可以是 整数 类型,也可以是 字符 类型。z字符 类型可以转化为对应的ASC2值。
int main(){char c;char e;c = 1;char d = '1';e = 'A';printf("c = %d\n", c);printf("d = %d\n", d);printf("e = %d\n", e);return 0;
}输出:
c = 1 // 表示整数类型
d = 49 // 表示字符类型, 字符'1'对应ASCll码的值是 49
e = 65 // 表示字符类型, 字符'A'对应ASCll码的值是 65
int main(){char c;printf("Enter an integer输入一个整数: ");//integer 整数scanf("%c", &c);printf("int = %d\n", c);printf("char = %c\n", c);return 0;
}输出:
Enter an integer输入一个整数: 1
int = 49
char = 1
int main(){if(49 == '1'){printf("ASC2 49 == char '1'\n");}else{printf("ASC2 49 != char '1'\n");}return 0;
}输出:
ASC2 49 == char '1'
scanf里有空格,表示读的空格不算做字符;没加空格时,空格就算做字符
(2)printf和scanf
对与printf,任何小于int的类型都会被转换成int,所以char和short会自动转成int。比如%f就可以变成double;
对于scanf,就必须按照输入的内容明确。比如short就用%hd,int就用%d,long long就用%ld,想输入整数char就不行,必须先int后再转成char类型。
(3)强制类型转换
最后要转成: (类型名称)值
int main(){double a =1.0;double b = 2.0;int i = (int)(a/b);printf("%d\n", i);
}输出:
0
(4)逃逸字符
比如 \" 表示双引号,因为在 " " 里面不能直接加双信号以引起歧义,所以用逃逸字符,类似的有:
(5)逻辑运算
age > 20 && age < 30 age < 20 || age > 30
表示 20 < age < 30 表示 age小于20或者age大于30
运算符优先级:
! > && > ||
(6)函数定义
函数的返回1个return
方式1:
return;
方式1:
return 某表达式或者变量 ;
函数的返回多个return
int max(int a, int b){if (a>b){return a;}else{return b; }
}
(7)没有返回值的函数
(1)void + 函数名
(2)不能使用return
(3)调用不能做返回值的赋值。
(8)函数的原型声明和函数的定义
函数原型的目的是告诉编译器这个函数长什么样子,内容包括: 返回类型 名称 参数
void sum(int begin, int end); //函数的原型
或者void sum(int, int); void sum(int begin, int end){ //函数的定义int i;int sum = 0;for (i = begin; i <= end; i++){sum += i;}
}
(9)函数的参数传递
(1)只能传值,不能传变量。比如下图中main里的变量a和b与swap函数在内部运行时的a和b和t之间是独立的,不会互相传递。
swap(a<b){int = 10;
}
i++; // 会报错因为前面的i在走出swap()代码块后就不再存在
(2)本地变量是定义{}内或者函数内,在这个代码块之前不作数,出了这个代码块也不再作数。
(3)代码块外面定义的变量在里面依然有效。另外相同变量名称,在代码块内就近,但出了块后还是恢复之前的情况。
i = 10;
swap(){int i = 0;printf("%d", i); // 输出 0
}
printf("%d", i); // 输出 10
(10)定义数组
格式:
数组内元素类型 变量名称[元素的数量]
int grades[100];
特点:(1) 所有元素具有相同数据类型;(2) 不能改变大小;(3) 元素在内存中连续排列;(4)可使用的元素范围是[0 , 数据大小 - 1 ]。
题目:输入数量不确定的[0, 9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束。
int main(){int num[10]; // int num[10] = {0}; int j = 0;for (j = 0;j < 10; j ++){num[j] = 0;}int n = 0;while(1){int input;printf("input number between 0 and 9\n");int count = scanf("%d", &input);if (input < 0 || input > 9 || count == 0){continue;}printf("input finish\n");num[input] = num[input]+1;printf("input number: %d\n", input);int i = 0;for (i = 0;i < 10; i ++){printf("count of %d: %d\n", i,num[i] );}}return 0;
}
(11)数组的大小(数组元素个数)
(1) 利用sizeof
由于int是4字节,以及sizeof(数组)是字段的长度,可得数组大小为: sizeof(a) / sizeof(a[0])。
但是当 数组 作为函数函数时,不能利用siezeof来计算数组的元素个数。
int a[6] = {2,4,5,6,7,8};
int b = sizeof(a);
printf("%d\n", sizeof(a)/sizeof(a[0]));输出:
6
int a[6] = {2,4,5,6,7,8};
int b = sizeof(a);
printf("%d\n", b);输出:
24
(11)数组的赋值
数组变量不能直接被复制,比如下面这个不对
int a[] = {2,4,5};
int b[] = a
这是不对的
而是通过每个元素的赋值实现
for (i = 0; i < length; i++){b[i] = a[i];
}
(12)二维数组
int a[3][5] 表示为a是一个3行5列的矩阵
a[0][0] ... a[0][4]...
a[2][0] ... a[2][4]
(13)二维数组的遍历
for (i =0; i< 3; i++){for (j =0; j < 5; j++){a[i][j] = i*j;}
}
(14)(&) 变量的地址
比如scanf("%d", &i);
int i; printf("%p", &i); //获取变量地址
// %p表示输出地址
// %lu的含义:long unsigned数据类型无符号长整数或无符号长浮点数
在堆栈里分配内存:地址分配顺序是由高位到低位,先写的地址更高,后写的地址更低。
对于电脑小端模式结构,导致数据的低位在内存低地址处,高位在地址高位处。
最左位为最高权位。相应的,最右边就是最低位。
(15)指针的值
指针就是保存地址的变量,指针变量的值就是内存的地址。p表示指针,*表示p这个指针指向的是int类型的变量,然后把i的地址交给p。*p是int。
(1) 普通变量的值是实际的值;(2) 指针变量的值是具有实际值的变量的地址。
(3) 传值,如壁虎断尾;传地址,跑得了和尚跑不了庙。
int i;
int * p = &i;
// p表示指针,*表示p这个指针指向的是int类型的变量
void f(int *p);
void f(int *p){print("*p = %p\n", *p);*p = 26;
}int main(){int i = 6;f(&i);g(i);return 0;
}输出:
&i = 0x7ffc94b0756cp = 0x7ffc94b0756c
(16)*指针(*p)
p是变量i的指针,p的值就是i的地址,*p用来访问变量i的值。
int k = *p;
*p = k+1;
void f(int *p);
void g(int k);
void f(int *p){printf("p = %p\n", p);*p = 26;
}
void g(int k){printf("k = %d\n", k);
}int main(){int i = 6;f(&i);g(i);return 0;
}输出:在经过了f()函数通过指针变量修改i的值后,变量i的值被修改了,p的值就是i的地址。
p = 0x7ffca728651c
k = 26
(17)指针应用1:交换变量的值
swap()函数,交换2个变量的值
void swap(int *pa, int *pb);{int t = *pa;*pa = *pb;*pb = t;
}
void swap(int *pa, int *pb); // 声明函数
void swap(int *pa, int *pb){ // 定义函数int t;t = *pa;*pa = *pb;*pb = t;
}int main(){int a = 5;int b = 6;printf("a = %d, b = %d\n", a, b);swap(&a, &b);printf("a = %d, b = %d\n", a, b); return 0;
}输出:
a = 5, b = 6
a = 6, b = 5
(18)指针应用2:函数返回多个值
找出最小值和最大值
void minmax(int a[], int len, int *min, int *max);
void minmax(int a[], int len, int *min, int *max){int i =0;*min = a[0];*max = a[0];for (i = 0; i < len; i ++){if (*min > a[i]){*min = a[i];}if (*max < a[i]){*max = a[i];}}printf("min value is %d, max value is %d\n", *min, *max);
}int main(){int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 16, 17, 21, 23, 55};int min, max;minmax(a, sizeof(a)/sizeof(a[0]), &min, &max);return 0;
}输出:
min value is 1, max value is 55
(19)指针应用3:函数参数里的数组就是指针
函数参数里的数组就是指针,数组的指针里写什么也没用。下面的数组a[]就是指针,所以也可以直接写成*a
void minmax(int a[], int len, int *min, int *max);void minmax(int *a, int len, int *min, int *max);
(1) int a[10]; int *p = a; //无需用&取地址
(2) 但是数组的单元表达的是变量,需要用&取地址
(3) []运算符可以对数组做,也可对指针做,都可以表示为:
*p == p[0]
p既可以是数组,也可以是指针
(4) 数组变量是const的指针,所以两个数组之间不能直接做赋值。可以用指针进行处理 *p
int a[] <==> int * const a
(20)指针与const(*const就是指针是const)
指针q是const
int * const q = &i; // q是const,也就是说q只能是指向i的地址
*q = 26; // q指向的地址,也就是变量i的值是26
q++ // ERROR
不能通过*p修改变量i的值
const int *p = &i;
*p = 26; // ERROR (*p)是const
i = 26; // OK
p = &j; // OK
const在*后表示指针不能被修改
int i;
int *const p = &i
const在*前表示不能通过指针修改变量的值
int i;
const int *p = &i;
int const *p = &i;
(21)指针运算
指针元素是sizeof的运算,比如指针p的int类型指针就是4的倍数,char类型指针就是1的倍数。
而*(p)的括号里的元素就取对应数组(由于是sizeof()的运算,所以int和char都是相同的结果),比如*p = a,则*(p+n) = a[n]。
int main(){
char ac[]= {2,3,4,5,6,7,8,9,0,11};
char *p = &ac[0];
char *p1 = &ac[5];
printf("p = %p\n", p);
printf("p1 = %p\n", p1);
printf("p1 - p = %p\n", p1 - p);int ai[]= {2,3,4,5,6,7,8,9,0,11};
int *q = &ai[0];
int *q1 = &ai[5];
printf("q = %p\n", q);
printf("q1 = %p\n", q1);
printf("q1 - q = %p\n", q1 - q);
}输出:
p = 0x7ffcb70513e6
p1 = 0x7ffcb70513eb
p1 - p = 0x5
q = 0x7ffcb70513b0
q1 = 0x7ffcb70513c4
q1 - q = 0x5
(21)指针运算的应用:*p++
用于数组类的连续空间操作:取出p所指的数据,之后再把p移到下个位置。
char ac[] = {0,2,4,1,5,9};
char *p = ∾
while (p != -1){printf("%d\n", *p++)
}输出ac[]数组中的每个元素
(22)指针类型强制转换
int *p = &i;
void * q = (void *)p;
通过指针p看i,i是int类型;而通过q看i,当i是void
(23)总结指针作用
(1) 需要传入较大的数据时用作参数。比如,对数组的操作。
(2) 动态内存分配,以及释放内存
int *a = (int*)malloc(n*sieze(int));
for (i = 0; i < n; i++){scanf("%d", &a[i]);
}
for (i =n-1, i>=0, i--){printf("%d", a[i]);
}
free(a);
(24)字符串特点
(1) 以“\0”结尾的一串字符,比如"Hello"数组长度是6,因为还有表示结束的\0。
(2) 字符串以数组的形式存在,可用数组和指针的形式访问。
(3) 不能用运算符对字符串做运算,最多可以用 “ ” 初始化字符数组。
(4) 通过数组的方式可以遍历字符串。
char *str = "Hello";
//指向字符串的指针
char word[] = "Hello";
//字符数组,内容是Hello
char line[10] = "Hello";
// 字符数组有10个字节,向里面放了Hello,占据6个字符
(25)字符串常量和数组字符串
字符串常量(不可修改):指针指向某个字符串
应用:处理字符串
(1) 只读字符串,不会改写 (1) 指针当做数组作为函数的参数 (2) 动态分配内存时,如果malloc获得,需要手动回收
char* s = "Hello, world!";
数组字符串(可修改):那个字符串就在我这里
应用:构造字符串
(1) 可修改 (2) 其变量的空间可自动回收
char s[] = "Hello, world!";
(26)char*是字符串吗?
本意表示指向连续字节的指针,并不能完全确定是字符串。只有当字符数组结尾有\0才能说是字符串。
(27)字符串输入
规定字符串最多读取多少个字符,下面是设置每个字符串最多读取7个字符
void f(void){char word[8];scanf("%7s", word);printf("%s\n", word);
}int main(){f();return 0;
}
(27)main()输入参数
int main(int argc, char const *argv[])整数(后面数组字符串数) 字符串数组
(28)getchar和putchar
ctl+C通过shell是直接断程序,crl+D是通过shell在缓冲区写入某个特定值然后getchar关闭
(29)字符串函数:strlen
size_t strlen(const char * s);
返回字符串的长度,但不包括结尾的0。自定义strlen函数如下:
size_t mylen(const char* s){int idx = 0;while (s[idx] != '\0'){idx++;}return idx;
}int main(int argc, char const *argv[]){char line[] = "Hello";printf("strlen = %lu\n", mylen(line));printf("strlen = %lu\n", strlen(line));return 0;
}
备注:strlen 的工作原理是从字符串的起始位置开始扫描,直到遇到 '\0'(空字符,即字符串的终止符)为止,返回这之前的字符数(不包括 '\0')。换行符 \n 和普通字符(如 'a'、'1' 等)一样会被计入长度。
(30)字符串函数:strcmp
比较两个字符串是否相等。
int strcmp(const char *s1, const char * s2);
自定义strlcmp函数如下:
int mycmp(const char *s1, const char *s2){int idx = 0;
//法1: 数组
// while (s1[idx] == s1[idx] && s1[idx] != '\0'){
// idx++;
// }//法2: 指针while(*s1 == *s2 && *s1 != '\0'){*s1++;*s2++;}return s1[idx]-s2[idx];
}int main(int argc, char const *argv[]){char s1[] = "abc";char s2[] = "abcc";printf("%d\n", mycmp(s1,s2));printf("%d\n", strcmp(s1,s2));return 0;
}
(31)字符串函数:strcpy
把第2个参数表达的字符串copy到第1个参数表达的字符串
char *strcpy(char *dest, const char *src);
// destination source
复制一个字符串
char *dst = (char *)malloc(strlen(src)+1);
// +1是因为strlen(src)不包括字符串结尾的0
strcpy(dst,src);
自定义strcpy函数如下:
char *mycpy(char *dest, const char *src){//法1:数组
// int idx = 0;
// while(src[idx] != '\0'){
// dest[idx] = src[idx];
// idx++;
// }
// dest[idx] = '\0';
// return dest;法2:指针char *rest = dest;while(*src != '\0'){*dest++ = *src++}*dest = '\0';return rest;
}
(32)字符串函数:strchr
在字符串中找第一个单个字符,返回的是指针,并指向找到的那一个字符,当返回的NULL表示没有找到。
char *strchr(const char *s, int c);
//从左边开始找
char *strrchr(const char *s, int c);
//从右边开始找
功能1:如何寻找第二个字符?
int main(int argc, char const *argv[]){char s[] = "hello";char *p = strchr(s,'l');printf("%s\n", p); // 第一次输出:llop = strchr(p+1,'l');printf("%s\n", p); // 第二次个输出:loreturn 0;
}输出:
llo
lo
功能2:把找到的字符复制到另一个字符串里?
int main(int argc, char const *argv[]){char s[] = "hello";char *p = strchr(s,'l');char *t = (char *)malloc(strlen(p)+1);printf("%s\n", t);free(t); return 0;
}输出:
llo
功能3:如何提取找到的字符的前面字符串?通过转移'\0'的位置修改了s的长度。
备注:*p = 变量的值;char *p = 地址;int *p = 地址
int main(int argc, char const *argv[]){char s[] = "hello";char *p = strchr(s,'l');char c = *p;*p = '\0';char *t = (char *)malloc(strlen(s)+1);strcpy(t,s);printf("%s\n", t);free(t); *p = c; return 0;
}输出:
he
(33)字符串函数:strstr
strstr在字符串中找字符串。strcasestr在字符串中找字符串过程中忽略大小写。
char * strstr(const char * s1, const char *s2);
char * strcasestr(const char * s1, const char *s2);
(34)enum 枚举
大括号里常量的类型是int,值是从0到n。
当需要排列的常量值时,定义枚举的意义就是给这些常量值名字。比const int方便。
功能1:如何自动计数枚举的元素数量
enum color {red, yellow, green, Numcolor};int main(int argc, char const *argc[]){应用1:定义数组,确定元素个数int color = -1;char *ColorNames[Numcolor] = {"red", "yellow", "green",};应用2:for循环,确定元素个数char *colorName = NULL;if(color >=0 && color < Numcolor){colorName = ColorNames[color];}return 0;
}
(35)结构体
复合的数据类型。声明结构类型(point)和结构变量(p1)。
写法1:
struct point{ // point 是虚指,声明新的结构类型int x;int y;
};
struct point p1; // p1 是实指,指结构变量写法2:
struct { int x;int y;
}p1;写法3:
struct point{ int x;int y;
}p1; // p1就是结构变量
结构体的初始化,取地址用&today
struct date{int month;int day;int year;
};方式1:
struct date today = {07,31,2014};方式2:
struct date thismonth = {.month = 7, .year = 2014};
结构体作为函数函数
int num(struct data today);
int num(struct data today){}
结构指向体的指针:两种表达方式 *p.month 和 p->month
struct data{int month;int day;int year;
}myday;
struct data *p = &myday;方式1
(*p).month = 12;
方式2
p->month = 12;
可用函数充取出的结构体
struct point* getStruct(struct point *p){scanf("%d\n", &p->);scanf("%d\n", &p->y);printf("%d, %d\n", p->x, p->y);return p;
}int main(int argc, char const * argv[]){struct point y = {0, 0};getStruct(&y);output(*getStruct(&y));return 0;
}
(36)结构体类型的数组
结构体类型的数组
struct data{int x;int y;
};struct data dates[100];
struct data dates[] = {{4,6},{3,4,5}};dates[100].x
(37)do while和while区别和联系
// do while是先做循环体,后判断
do{<循环体语句>
}while(<循环条件>);// while 是先判断,后做
while(<循环条件>){<循环体语句>
}
(38)