资阳网站设计,什么做的网站,dw做网站背景音乐,怎样在手机上面建设网站常量和常量表达式的区别 #define N 4;又是常量#xff0c;又是常量表达式#xff0c;其在编译期预处理阶段就会直接替换 const int M 5;只是常量#xff0c;不是常量表达式 #xff0c;其是存储在一块内存区域之内的#xff0c;但是存储的值不能改变 常量表达式#xff…常量和常量表达式的区别 #define N 4;又是常量又是常量表达式其在编译期预处理阶段就会直接替换 const int M 5;只是常量不是常量表达式 其是存储在一块内存区域之内的但是存储的值不能改变 常量表达式在编译期间就可以直接求值的表达式 常量在运行期间不能改变的 常量表达式可以用于指定数组长度还可以用于switch语句的标签 #define N 5
const int M 4;
int main() {int arr[N];//正确int arr[M];//错误M不是常量表达式int i;scanf(%d,i);switch (i) {case N://正确printf(N %d\n,N);break;case M://错误M不是常量表达式printf(M %d\n,M);break;}
}标准输入输出 scanf本质上是一个“模式匹配函数”试图把输入的字符与转换说明进行匹配从左到右一次处理转换说明如果成功则继续处理后面的字符串如果失败则立即返回返回值表示处理转换说明成功的个数
int main() {int i,f;int k scanf(%d %d,i,f);//输入的必须为int类型float d;//尽量避免下面这种写法k scanf(i %d,d %f,i,d);//输入格式必须为--- i 17,d 3,1415---这样的格式否则字符串匹配不上也我无法输入return 0;
}转换说明 1表示匹配的规则 2表示将字符数据转换成对应的二进制数据
格式串普通字符其他字符精确匹配空白字符匹配任意多个空白字符包括零个
注意事项 scanf匹配%d,%f进行数值匹配的时候会匹配前面的空白字符
读写整数 %u 无符号十进制整数 %o 无符号八进制整数 %x 无符号十六进制整数 %d 有符号十进制整数 读写短整数在u,o,x,d前面添加h(short) 读写长整数u,o,x,d前面添加l 读写长长整形在u,o,x,d前面添加ll
int main() {unsigned a;scanf(%u,a);printf(%u\n,a);printf(%o\n,a);printf(%x\n,a );short s;scanf(%hd,s);printf(%hd,s);
}浮点数数据类型 浮点数包括float4字节,double8字节,long double(用于高精度计算中一般用不到)
浮点数常量又多种表示方法要么包含小数点要么包含字母E(e) 浮点数常量默认是double如果需要表示float ,在浮点数常量后买你添加字母F(f)
57.0
57.
5.70e1( 表示以10为底的数表示5.70*10^1)
.57e2读写浮点数
%f : float
%lf : double(注意l不能大写)
double i;
scanf(%lf,i);
printf(%lf,i);字符数据类型 char类型大小为1个字节并且使用ASCII编码表示ASCII编码用7位表示128个字符最高位都为0 C语言把字符类型当作小的整数类型来使用因此可以对字符执行算术运算和比较运算
int i a;
char ch A;
ch ch 1;
//将大写转换位小写
if(ch A || ch Z) {ch ch a - A;
}不能直接输入的字符----转义字符 字符转义序列
\a 警报响铃
\b 回退
\f 换页
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符
\\ backslash
\? question mark
\ single quote
\ single quote数字转义序列 八进制形式表示以\开头后面接最多3个八进制数 \o, \101 ‘A’ 十六进制表示形式以\x开头后面接十六进制数字 \x0,\x41 ‘A’
printf(%c,\101);//输出A
printf(%c,\x41);//输出A字符处理函数 character classification分类函数 需要导入头文件ctype.h characet manipulation操作函数
读写字符 scanf/printf配合%c来读写字符 注意事项%c不会忽略前面的空格字符,可以在%c前面添加一个空格来实现忽略前面的空格这样的操作还可以忽略换行
scanf( %c,ch);getchar(把字符写到stdin中)/putchar(把字符写到stdout中)效率比scanf/printf效率要高 ch getchar();//读入字符到stdin
putchar(ch);//将字符传到stdout布尔类型 c99中定义了布尔类型在stdbool.h头文件中
#include stdbool.hbool 类型本质上是无符号整数类型 注意给布尔类型变量复制非零会得true零会得到false
类型转换 1.为什么需要进行类型转换 计算机硬件只能对相同类型的数据运算计算机是没有数据类型的概念的 2.何时会发生类型转换 给定多的数据类型与需要的数据类型不匹配时 3.如何进行类型转换 隐式类型转换编译器做的类型转换不需要程序员手动指明 short ,char只要参与运算就会转换位int类型进行运算整数提升 int和long类型进行运算int类型会转换为long类型进行运算 long类型和longlong类型运算long类型会转换位longlong类型 longlong类型和float类型运算longlong类型会转换为float类型 float类型和double类型运算float类型会转换位double类型 注意不要将有符号整数和无符号整数进行运算因为他们会进行类型转换就会使用补码进行运算运算的结果可能就会出错 显示转换强制类型转换可以让程序员更精确的控制类型转换
//强制类型转换的好处
//1.使用显示转换实现计算浮点数的小数部分
double d 3.14,f;
f d - (int)d;
//2.增强代码的可读性表明肯定会发生的转换
float f 3.14;
//...
int i (int)f;
//3.对类型转换进行更精确的控制
int divided 4,divisor 3;
double quo;
//quo divided / divisor;//1.0
quo (double)divided / divisor//1.3333333....//4.避免溢出
long long millisPerday 24*60*60*1000;//每天的秒
long long nanosPerday (long long)24*60*60*1000*1000*1000;//每天的毫秒在前面加上强制类型转换就不会出错
printf(%lld\n,nanosPerday/millisPerday);//没有加long long强制类型转换时输出-21因为24,60,1000这些数据都是int类型的数据虽然我们最终的结果没有超过longlong类型的长度但是超过了int类型的长度int类型进行运算会先将结果存储在int类型的长度变量里再将其转换为longlong类型因此变换之前就超过int类型的长度就会出先计算错误
Typedef 我们可以使用typedef给类型起别名 格式
typedef type_name alias;
typedef int Bool;//Bool为int数据类型的别名1typedef和宏定义的区别 宏定义是在预处理阶段进行处理的(简单的文本替换)编译器是不能够识别宏定义的因此如果宏定义中有错误编译器就不能给出一些友好提示编译器能够识别typedef定义的别名如果发生错误就能给出一些友好提示信息 定义类型使用typedef不要使用宏定义 2给类型起别名的好处 a.增加代码的可读性 b.增加代码的可移植性 sizeof运算符 在编译阶段就进行计算的因此他是一个常量表达式可以表示数组的长度 作用计算某一类型的数据所占内存空间的大小以字节为单位
int i 3;
//语法
sizeof(type_name);
int arr[sizeof(i)];//正确表示运算符以及优先级 注意事项 1、-、*、/可以用于浮点数但是%必须要求两个操作数都是整数 2 两个整数相除其结果为整数 3a%b的结果可能为负符号与a的符号相同满足a%b a - (a/b)*b //取模
bool is_odd(int n) {return n%2 1;//错误的如果n -1,那么会返回true
}
bool is_odd_1(innt n) {return n%2 ! 0;
}
//更高效算法
bool is_odd_2(int n) {return n 0x1;//让n和1进行按位与最后是结果是1那么就是奇数结果为0那么就是偶数
}位运算符 、、、|、^、~ 移位运算符 ij:就左移j位在左边补0 ij:将i右移j位若i为为无符号整数或者非负数则左边补0若i为负数他的行为是由实现定义的有的左边会补0有的左边补1 为了代码的可移植最好不要对有符号整数进行移位运算 //s左移两位
short s 13;
printf(s 2 %d\n,s2);//52
//左移两位0000 0000 0000 1101 --- 0000 0000 0011 0100
//若没有发生溢出左移J位相当于乘以2^J//s右移两位
short s1 13;
printf(s1 2 %d\n,s2);//3
// 0000 0000 0000 1101 --- 0000 0000 0000 0011
//右移j位相当于除于2^j向下取整按位运算符 short i 3,j 4;
//按位取反
~i取反:0000 0000 0000 0011 --- 1111 1111 1111 1100 (-4)
ij按位与:0000 0000 0000 0011 0000 0000 0000 0100 --- 0000 0000 0000 0000 (0)
i|j按位或:0000 0000 0000 0011 | 0000 0000 0000 0100 --- 0000 0000 0000 0111 (7)
i^j异或:0000 0000 0000 0011 ^ 0000 0000 0000 0100 --- 0000 0000 0000 0111 (7)
//异或性质
a ^ 0 a
a ^ a 0
a ^ b b ^ a 交换律
a^(b^c) (a^b)^c 结合律 //1.如何判断一个整数是否为2的幂
//方法1
bool isPowerO2(unsigned int n) {unsigned int i 1;while(i n) {i 1; }return i n;
}
//方法2
bool is PowerOf2(unsigned int n) {return (n n-1) 0;
}
//2.的幂的二进制表示有神什么特征只有一个1
// 0000 0000 0100 0000 0000 0000 0011 1111 --- 0000 0000 0000 0000 (0)//2.给定一个不为零的整数找出值为一且权重最低的位
//输入0011 0100 输出4
((n ^ n-1) 1) 1
n (-n) : 0011 0100 (n) 1100 1100 (-n) --- 0000 0100 (4)//3. 给定一个整数数组里面的数都是成对的只有一个数例外请找出这个数
int arr[] {1,2,3,4,5,4,3,2,1};
printf(%d\n,findSingleNumber(arr,9));int findSingleNumber(int arr[],int a) {int singleNum 0;for(int i 0;i n;i) {singleNum ^ arr[i];}return singleNum;
}数组 为什么在大多数语言中数组的索引都是从0开始的 原因是方便寻址 若索引从0开始i_addr base_addr i*sizeof(element_type) 若索引从1开始i_addr base_addr (i-1)*sizeof(element_type);每次寻址多执行一次减法运算 为什么数组的效率一般会优于链表 1数组的内存空间是连续的而链表的内存空间不连续数组可以更好的利用CPU的cache预读局部性原理 2数组只需要存储数据链表不仅经要存储数据还需要存储指针域数组的内存使用率更高
数组的声明int arr[size] 注意事项size必须是整形的常量表达式在编译期间能计算出数组的大小 数组的初始化
#define SIZE(a) (sizeof(a)/sizeof(a[0]))//计算数组长度
int arr[10] {0,1,2,3,4,5,6,7,8,9};
int arr[10] {1,2,3};//其余䛾初始化为0{1,2,3,0,0,0,0,0,0,0}
int aar[10] {0};//将数组所有元素初始化为0
int arr[] {1,2,3,4,5,6,7,8,9,10};//数组长度编译器自行推断这里的长度是10二维数组的初始化 二维数组是以行优先的形式进行初始化的
int matrix[3][4] {{1,2,3,4},{2,2,3,4},{3,2,3,4}};//
int matrix[3][4] {1,2,3,4,2,2,3,4,3,2,3,4};//不建议
int matrix[3][4] {0};
int matrix[][4] {{1,2,3,4},{2,2,3,4},{3,2,3,4}};//编译器自行判断行的大小
//注意事项不能省略列的大小常量数组
const int arr[10] {0,1,2,3,4,5,6,7,8,9};//数组元素不能发生改变存放静态数据生成随机数
srand((unsigned)time(NULL));//利用时间设置随机数种子
for(int i 0;i 10;i) {printf(%d\n,rand());//rand默认随机数种子为1
}一维数组作为参数传递的时候会退化为指向第一个元素的指针好处 1可以避免数据复制 2可以修改原始数组的值 3函数调用会更加灵活 数组作为参数传递的时候会丢失类型信息数组长度
//传递数组的时候将数组长度一并传递
int sum_arr(int arr[],int length);二维数组在作为参数传递的时候不能省略列的信息只能省略行的信息
int main() {int materix[3][4] {{1,2,3,4},{2,2,3,4},{3,2,3,4}};printf(%d\n,sum_arr(materix,3));return 0;
}
int sum_arr(int materix[][4],int n){int sum 0;for(int i 0;i n;i) {for(int j 0;j 4;j) {sum materix[i][j];}}return sum;
}如果我们要在main函数里面退出程序可以使用return语句当我们不在 main函数里面但是想退出程序我们可以使用exit函数exit(EXIT-SUCCESS) 或者exit(0)—正常退出exit(EXIT-FAILUE)或exit(1)—异常退出 #define EXIT-SUCCESS 0
#define EXIT-FAILUE 1指针
计算机最小的寻址单位字节 变量的地址变量第一个字节的地址 指针建档的来说指针就是地址 指针变量存放地址的变量有时候把指针变量称为指针
声明指针时需要指明基础类型
int *p;//p为变量名变量的类型是int *而不是int 两个基本操作取地址运算符和解引用* 取地址运算符int i 1; int *p i;取出变量i的地址赋值给int * 解引用运算符通过解引用运算符访问指针指向的对象 printf(%d\n,*p);//1 p相当于i的别名修改p相当于修改i i直接访问访问一次内存 *p间接访问访问两次内存 野指针 未初始化的指针或者是指向位置区域的指针 野指针会造成位置错误
//野指针
int *p;
int *q 0x7f;指针和数组 指针的算术运算 1指针加上一个整数 2指针减去一个整数 3两个指针相减指向同一个数组里面的元素 指针的算术运算是以元素的大小为单位的而不是以字节为单位 int arr[10] {0,1,2,3,4,5,6,7,8,9};
int* p arr[2];
int* q p3;
p 6;
printf(*p %d,*q %d\n,*p,*q);//85
------------------------------------------------------------------------------//指针的比较两个指针指向同一个数组的元素
// p - q 0 p q
// p - q 0 p q
// p - q 0 p q
int* p arr[2];
int* q arr[5];
printf(p - q %d\n,p - q);//-3
printf(q - p %d\n,q - p);//3
------------------------------------------------------------------------------//用指针处理数组
int sum 0;
for(int* p arr[0]; p arr[10];p){//arr[10]只会计算arr[10]的地址不会访问arr[10]因此不会发生数组越界sum *p;
}
printf(sum %d\n,sum);//45
------------------------------------------------------------------------------int sum 0;
int* p arr[0];
while(p arr[10]) {sum *p;
}
printf(sum %d\n,sum);*和的组合 *p,*(p)表达式的值为*p,p自增 (*p)表达式的值为*p,*p自增 *p,*(p)表达式的值为*(p1),p自增 *p,(*p)表达式的值为*p1,*p自增 *和–的组合和上面类似 数组名作可以作为指向索引为0的元素的指针
int arr[10] {0,1,2,3,4,5,6,7,8,9};
*arr 100;
*(arr 7) 700;
//arr[10] {100,1,2,3,4,5,700,7,8,9}
int sum 0;
for(int*p arr;p arr9;p) {sum *p;
}
sum 0;
for(int i 0;i 10;i) {sum (arri);
}指针也可以作为数组名来使用可以对指针使用[]运算符 p[i] *(pi) 总结指针和数组之间的关系 1可以利用指针处理数组指针的算术运算 2数组名可以做指向该数组索引为0元素的指针 3指针也可以作为数组名可以对指针使用[]运算符p[i] *(pi) 字符串
C语言是通过字符数组存储字符串字面量的 字符串的初始化 读写字符串 写printf %s, puts---string char name[] Allen;
printf(%s\n,name);
puts(name);//写完字符串后会在后面添加额外的换行符 puts(name)等价于printf(%s\n,name)读scanf %s,gets,gets_s #define N 100
char name[N];
scanf(%s,name);//读取规则会跳过前面的空白字符读取字符存入到数组中知道遇到空白字符位置然后会在后面添加空字符’\0‘
//注意事项
//(1)永远不会包含空白字符
//(2)sacnf不会检查数组越界
gets(name);//不会跳过前面的空白字符读取字符存入到数组中知道遇到换行符为止然后再后面添加’\0‘
//注意事项
//同样gets也不会检查数组越界
gets_s(name,n);//n表示数组的长度所以最多能够读n-1个字符第n个位空字符’\0‘会检测数组越界遇到换行符渡或者检测到数组越界就停止读入字符串的库函数 1size_t strlen(const char* s)获取字符串的长度不包含空字符’\0‘,使用const常量指明再strlen中不会修改s指向的内容传入参数
printf(%d\n,strlen(abc));//3
printf(%d\n,strlen());//02int strcmp(const char* s1,const char* s2)比较字符串按照字典顺序比较s1和s2“abc” acb,abc abd,abc abcd, 如果s1 s2则返回正数 如果s1 s2则返回零 如果s1 s2则返回负数
3char* strcpy(char* s1,const char* s2)【不安全的因为不会检查数组越界】把s2指向的字符串复制到s1指向的数组中不会检查数组越界s1变量没有携带const关键字表明通常会在函数中修改s1指向的内容传入传出参数 char* strncpy(char* *dest,const char* src,size_t count)【安全的】指定目标数组中可以接收的大小
char s1[10];
strncpy(s1,Hello,4);//s1 Hell不会写入空字符\0
strncpy(s1,Hello,6);//s1 Hello \0
strncpy(s1,Hello,8);//s1 Hello \0 \0 \0 \0 后面会添加额外的空字符直到写入8个字符4char* strcat(char* dest,const char* src)将字符串src的内容追加到字符串dest的末尾并返回dest不会检查数组越界
5char* strncat(char* dest,const char* src,size_t coant)会在dest末尾添加src中的coant个字符,因为strncat总是会写入‘\0’因此我们一般会这样调用strncat(s1,s2,sizeof(s1)-strlen(s1)-1)给空字符预留一个空间
char s1[10] Hello;
strcat(s1, world);//s1 Hello world \0
strncat(s1, world,2);//s1 Hello world \0
strncat(s1, world,7);//s1 Hello world \0字符串惯用法 自己编写strlen函数
size_t my_strlen(const char* s) {size_t n;for(int n 0; *s ! \0;s) {n;}return n;
}
//改进后
size_t my_strlen(const char* s) {char *p s;while(*p);return p-s-1;//s指向了空字符后面的字符
}
size_t my_strlen(const char* s) {char *p s;while(*p){p;}return p-s;
}自己编写strcat函数
char* my_strcat(char* s1,const char* s2) {char* p s1;while(*p) {p;}while(*s2 ! \0) {*p *s2;p;s2;}*p \0;return s1;
}
//改进后
char* my_strcat(char* s1,const char* s2) {char* p s1;while(*p) {p;}while(*p *s2);return s1;
}字符串数组
char* planets[] {Mecury,Venus,Earth,Mars,Jupitor,Saturn,Uranus,Neptune,Pluto};结构体
c语言的结构体相当于其他高级语言中的类c语言只能在结构体中定义数据
//定义结构体
struct students{int id;char name[25];bool gender;int chinese;
}
int main() {//1.初始化结构体struct students t1 {1,liuyifei,false,100};struct students t2 {2,huasheng,true};//未被初始化的成员都会被赋值未0//2.对结构体的操作printf(name %s\n,t1.name);
}
//为了避免拷贝数据我们往往会传递一个指向结构体的指针
void printf_student(struct student_s* s){printf(%d %s %d %d\n,(*s).id,(*s).name,(*s).gender,(*s).chinese)
}
//C语言程序一般时通过指针去引用结构体的C语言提供了一个特别的运算符-
void printf_student(struct student_s* s){printf(%d %s %d %d\n,s-id,s-name,s-gender,s-chinese)
}
//3.使用typede为结构体起别名
typedef struct students{int id;char name[25];bool gender;int chinese;
} student_s;//别名student_s注意事项当结构体作为参数或者返回值时会拷贝整个结构体中的数据
指针的高级应用
1动态内存分配 在头文件stdlib.h定义了三个动态内存分配的函数(在堆上分配内存空间)
void* malloc(size_t size);分配size个字节的内存不会对分配的内存块清零若分配不成功返回空指针void* calloc(size_t num,size_t size);为num个元素分配内存空间每个元素的大小为size个字节并对内存块清零若分配不成功返回空指针void* realloc(void* ptr,size_t size);调整先前分配内存块的大小如果分配成功返回指向新内存 注意事项ptr应该指向先前使用动态内存分配函数分配的内存块
空指针不指向任何对象的指针(用宏NULL表示其值为0)
#include stdio.h
#include stdlib.hint main(void) {char* s1 Hello ;char* s2 world! ;char* s my_strcat(s1,s2);puts(s1); //Hello puts(s2); //world!puts(s);//Hello world!return 0;}char* my_strcat(const char* s1,const char* s2){char* s (char *)malloc(strlen(s1) strlen(s2) 1);if(s NULL) {//未分配成功//做错误处理return NULL;}strcpy(s,s1);//将s1赋值到s中strcat(s,s2);//将s2复制到s末尾return s;}//以上代码没有释放内存空间如果申请的内存空间没有释放就可能造成内存泄漏现象 分配的内存没有指针指向它导致不能访问的内存被称为垃圾 如果程序中存在垃圾这种现象称为内存泄漏 如何避免内存泄漏 使用void free(void* ptr);进行内存释放ptr必须是之前申请的内存空间的指针
2指向指针的指针(二级指针)
#include stdio.h
#include stdlib.h
typedef struct node_s{int val;struct node_s* next;
} Node;
void add_to_list(Node** ptr_list,int val);
int main(){Node* list NULL:add_to_list(list,1);add_to_list(list,2);add_to_list(list,3);add_to_list(list,4);return 0;
}
void add_to_list(Node** ptr_list,int val){Node* newNode (Node*)malloc(sizeof(Node));if(newNode NULL) {printf(Error:malloc failed in add_to_list.\n);exit(1);}//头插法newNode-val val;newNode-next *ptr_list;*ptr_list newNode;
}3指向函数的指针(函数指针)
#include stdio.h
#include math.h
#define PI 3.1415926
double average(double (*f)(double),double a,double b);
int main(void){double avg average(sin,0,PI);printf(%lf,avg);return 0;
}
double average(double (*f)(double),double a,double b){return (*f)((ab)/2);
}
//简便写法
//double average(double f(double),double a,double b){
// return f((ab)/2);
//}qsort void qsort(void *ptr,size_t count,size_t sizre,int (*comp)(const void *,const void *)) ptr----指向要排序的数组 count----数组中元素的个数 size----元素的大小 comp----比较函数如果第一个参数大于第二个参数返回值正值如果第一个参数等于第二个参数返回零如果第一个参数小于第二个参数返回负值 可以对任何类型的数组进行排序不管元素类型是什么 #include stdio.h
#include string.h
typedef struct student_s{int number;char name[25];int chinese; int math;int english;
} Studet;
int compare(const void* p1,const void* p2);#define SIZE(a) (sizeof(a)/sizeof(a[]0]))
int main(void) {Student students[5] {{1,liuyifei,100,100,100},{2,wangyuyan,99,100,100},{3,zhaolinger,100,99,100},{4,xiaolongnv,100,100,99},{5,baixiuzhu,100,100,99}};qsort(students,SIZE(students),sizeof(Student),compare);return 0;
}
//比较规则总分从高到低语文成绩高--低数学成绩高--低英语成绩高--低姓名字典顺序从小到大进行比较
int compare(const void* p1,const void* p2) {Student* s1 (Student*)p1;Student* s2 (Student*)p2;int total1 s1-chinese s1-english s1-math;int total2 s2-chinese s2-english s2-math;if(total1 ! total2) {return total2 - total1;}if(s1-chinese ! s2-chinese){return s2-chinese - s1-chinese;}if(s1-math ! s2-math){return s2-math - s1-math;}if(s1-english ! s2-english){return s2-english - s1-english;}return strcmp(s1-name,s2-name);
}