c语言笔记
c语言
第一个c程序
#include <stdio.h>
/*
头文件
函数声明和类型的定义
*/
/*
入口函数:有且仅有一个
*/
int main(void)
{
// 标识符:变量名,函数名,类型名.....
// 命名规范:有字母数字下划线组成,数字不开头,区分大小写,避开c关键字
printf("早上好!\n"); // 输出
返回值正确返回输出的字符总数,错误返回负值
return 0;// main不是mian 特殊接口,结束标志着进程的结束,return
// return 值标志进程的终止状态
}
格式占位符
#include <stdio.h>
int main(void)
{
// 存储数值---》定义相应类型的变量
int num;// signed默认 unsigned(无符号)
num = 1;
/*
格式占位符:
%d int
%hd short
%ld long
%c char
%p 地址
%o 八进制
%x 十六进制
%10d 10输出数值所占宽度
*/
printf("num = %d\n", num);
/*
转义字符
\n 换行
\b 退格
\r 回车(回到行首)
\t 水平制表符(缩进一个tap)
*/
printf("hello\bwo\trld\rhi\n");
printf("%ld\n", sizeof(short));
printf("%ld\n", sizeof(int));
printf("%ld\n", sizeof(long));
printf("%ld\n", sizeof(long long));
printf("%ld\n", sizeof(float));
printf("%ld\n", sizeof(double));
printf("%ld\n", sizeof(long double));
return 0;
}
C基本类型
变量:值可以随时改变
常量:只读
数据类型
-
基本类型
- 整形
- short 2
- int 4
- long 8
- long long 8
- 字符类型
- char 1
- 存储单个字节
- char 1
- 实型
- float 4
- double 8
- long double 16
- 整形
-
构造类型
- 枚举
- 结构体
- 共用体
-
指针类型
- 存储地址类型
-
空类型
- void
-
有符号和无符号
- signed(默认)
- unsigned
-
类型转换
-
#include <stdio.h> int main(void) { char ch1 = 'a'; int var; float f; var = ch1; // 自动类型强转(隐式转换) // 显式转换 (数据类型)变量 printf("var:%d\n", var); printf("%d\n", var + ch1); f = var + ch1; printf("f:%f\n", f); f = 1.9; printf("%d\n", (int)f+1);//强转 return 0; }
-
变量定义
- type name;
- 标识符:变量名,函数名,类型名
- 命名规范:由字母、数字、下划线组成,数字不开头,避开c关键字,区分大小写
- "顾名思义"
初始化
- type name = value; // 函数内变量未初始化,值是随机
运算符
Operator Associativity Notes
() [] -> . ++ -- left to right [1]
! ~ ++ -- + - (type) * & sizeof right to left [2]
* / % left to right
+ - left to right
<< >> left to right
< <= > >= left to right
== != left to right
& left to right
^ left to right
| left to right
&& left to right
|| left to right
?: right to left
= += -= *= /= %= <<= >>= &= ^= |= right to left
, left to right
控制语句
条件语句
-
if
if (变量/表达式) { // 循环体多条语句,一定要由{} } -
if ... else
if (变量/表达式) { // 条件为真 } else { // 条件为假 } -
if... else if ....else if... else
if (变量/表达式) { } else if (变量/表达式) { } else { } -
switch
switch(变量/常量表达式) { case 常量值: break; case 常量值: break; default: break; }
循环语句
-
for
-
for (表达式1; 表达式2; 表达式3) { 循环体语句4; } 表达式1:循环变量初始化,只有进入循环执行一次 表达式2:循环条件 表达式3:循环变量的改变 循环:1 2 4 3 2 4 3 都可缺省 -
while
-
while (条件) { 循环体语句; } -
do .. while
-
do { 循环体; }while(条件); 多用于错误校验 -
continue和break
- continue 终止本次循环,继续下一次
- break 终止最近循环
-
goto 无条件跳转
- 标号ERROR:
函数
基本组成
功能实现封装到小的接口模块内
-
定义
-
定义: 返回值类型 函数名(参数列表) { 函数体; }- 函数名---》标识符 顾名思义
- 返回值类型--》默认是int
- 如果有返回值,return
-
声明
- 使用已经定义好的函数借口之前需要声明
- 返回值类型 函数名(参数列表类型);
-
调用
- 函数名(参数)
- 函数的传参---》值传递过程
变量类型
-
局部变量:在函数体内定义的
- 作用域与生存周期:从定义开始到函数结束
- 未初始化值随机
-
全局变量:在函数体外定义的
- 作用域和生存周期:整个文件
- 未初始化值0
-
修饰变量关键字
-
全局变量--->extern(默认) static
- 定义在函数体外,整个程序
-
局部变量---->auto(默认) static const register volatile
-
定义在函数体内,函数内
-
static:
- 修饰全局变量,限制其作用域在本文件中
-
修饰函数,限制作用域在本文件中调用
- 修饰局部变量,局部静态变量,延长生命周期到整个文件,但作用域不变
-
只初始化一次
-
未初始化值未0
-
const 保护变量
- read-only 变量
-
register
- 寄存器变量
-
volatile
-
易失变量:防止编译优化
-
int num = 1; num == num; // 永远成立
volatile int num = 1;
num == num;// 不能确保永远成立 -
-
extern
- 外部变量
-
-
块变量:定义在语句块if for while语句块内, {}
-
#include <stdio.h>
extern int glob; // 全局变量, 未初始化值是0---->bss
static int glob2 = 2; // 全局变量, 数据段,static 限制的是作用域在本文件
static void test(void);
int main(void)
{
auto int var; // 局部变量, 未初始化,值随机--->stack
const int num = 5;
num = 100;
for (int i = 1; i < 10; i++)
printf("i:%d\n", i); // 块变量, 栈
test();
test();
test();
test();
return 0;
}
static void test(void) // 限制作用域在本文件
{
static int n;
printf("n:%d\n", n);
n ++;
}
c存储空间布局
- 栈
- 局部变量,函数的形参
- 未初始化,值随机值
- 生命周期随着函数调用的结束而结束
- 堆
- 用户自行申请和释放
- bss
- 未初始化的局部静态变量和全局变量
- 初始值为0
- 数据段
- 已经初始化的局部静态和全局变量
- 生命周期都是从定义开始到进程结束
- 文本段
特殊的函数
-
递归函数
-
在函数体内调用函数本身
-
找到递归的终止条件
-
找到递归点
-
#include <stdio.h> int fib(int n); int main(void) { for (int i = 1; i < 20; i++) { printf("%d ", fib(i)); } printf("\n"); return 0; } /* 终止条件 递归点 */ int fib(int n) { if (n == 1 || n == 2) return 1; if (n < 1) return -1; return fib(n-1) + fib(n-2); }
-
-
递归用于解决复杂问题
- 汉诺塔
- 迷宫
- 树
-
-
变参函数
- ...
- printf(3) / scanf(3)
宏
-
define NAME value
-
宏在gcc预处理阶段纯替换
-
宏函数
-
参数加括号
-
末尾无分号
-
建议do {}while(0)
-
/* #开头的---》预处理指令 gcc四个步骤 1. 预处理:头文件的展开,宏的替换.... -E 2. 编译:翻译成汇编 -S 3. 汇编:生成目标文件 -c 4. 链接:动态链接(库函数) */ #include <stdio.h> #define NUM 10 #define SQUARE(x) (x)*(x) #define SWAP(x, y) \ do {\ typeof(x) t; t = x; x = y; y = t;\ }while(0) int main(void) { int i; int score; int m, n; printf("good morning\n"); #if 0 for (i = 0; i < NUM; i++) scanf("%d", &score); #endif i = 5; printf("%d\n", SQUARE(i+2)); // i+2*i+2 m = 10; n = 20; SWAP(m, n); printf("m:%d, n:%d\n", m, n); return 0; } -
必须一行
-
太长续行
-
x 将x替换为字符串
-
‘##’ 连接
-
-
if 常量 / 常量表达式 #endif
-
ifdef 常量 #endif
-
ifndef xxx #endif
数组
基本知识
- 定义:相同类型元素的集合
- type name[n];
- 赋值
- 每一个成员变量通过下标值,从0开始
- for (i = 0; i < n ; i++)
- 初始化
- type name[n] = {}; 全部初始化为0
- type name[n] = {1}; 第一个元素为1,其他为0
- type name[] = {1,2,3}; 成员个数可缺省,大小取决于给定的值
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
/*
数组:相同类型元素的集合
定义:type name[nmembs];
赋值: name[0],name[1], name[nmembs-1];
初始化:定义的同时赋值
type name[nmembs] = {};
遍历:访问每一个元素,同赋值
*/
int main(void)
{
int arr[5]; // 定义
int i;
int arr2[3] = {1,3}; // 初始化
// srand(time(NULL));
srand(getpid());
// 赋值
for (i = 0; i < 5; i++) {
arr[i] = rand() % 100;
}
// 遍历
for (i = 0; i < 5; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
数组名
- 数组首地址---》首元素的地址
- 常量值
- type *
- 类型决定了步长+取值字节大小(sizeof(type))
- int*步长---》sizeof(int)
只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量。而只有以下两种情况,才不被当做指针常量:
- sizeof(数组名):返回数组长度(所占的字节数,不是数组元素个数),而不是指向数组的指针的长度。
- &数组名:产生一个指向数组的指针,而不是一个指向某个指针常量的指针。
运算符
- a[b]--->*(a+b)--->b[a]
-
- 取数组中的元素
- name[index]
- *(name+index)
- 元素地址
- name + index
- &name[index];// &*(name+i)
- *&抵消
- 数组索引值[0,n-1],一定不要越界!!!!
排序
- 冒泡排序
- 相邻元素两两比较,不符合大小顺序则交换
- 每比较一整趟,使得一个元素有序。如果n元素,则比较n-1趟
- for (i = 0; i < n-1; i++)
- 每一趟比较的都是无序序列中的所有元素
- for (j = 0; j < n-i-1; j++)
- 选择
- 依次选择待排序的位置,该位置的元素与所有无序序列中的而元素一一比较,不符合大小关系则交换
- 选
- for (i = 0; i < n-1; i++)
- 比
- for (j = i+1; j < n; j++)
- 直接插入排序
- 将所有元素分有序区和无序区,从无序序列中依次选择每一个元素,向有序序列中插入
- 无序区中选带插入的元素
- for (i = 1; i < n; i++)
- 有序区待比较的元素
- for (j = i-1; j >= 0; j --)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define N 10
int main(void)
{
int arr[N] = {};
int i, j;
int t;
int max;
srand(getpid());
for (i = 0; i < N; i++) {
arr[i] = rand() % 100;
printf("%d ", arr[i]);
}
printf("\n");
// 冒泡排序
for (i = 0; i < N-1; i++) {
// 每一趟要比较的元素的范围
for (j = 0; j < N-i-1; j++) {
if (arr[j] > arr[j+1]) {
t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
}
// 选择排序
for (i = 0; i < N-1; i++) {
// arr[i]选择的
max = i;
for (j = i+1; j < N; j++) {
if (arr[j] > arr[max])
max = j;
}
if (max != i) {
t = arr[i];
arr[i] = arr[max];
arr[max] = t;
}
}
// 直接插入排序
for (i = 1; i < N; i++) {
t = arr[i];
for (j = i-1; j >= 0; j--) {
if (t < arr[j]) {
arr[j+1] = arr[j];
} else
break;
}
// j < 0 || break
arr[j+1] = t;
}
for (i = 0; i < N; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
查找
有序数组---》二分查找(折半查找)
产生随机数
-
rand(3)
-
srand(3)
-
提供种子:time(2)---》获取时间戳 / getpid(2)
字符数组和字符串
c语言中没有字符串类型,用字符数组来存储字符串
-
字符串:多个字符组成并由'\0'作为结束标志
-
字符串初始化数组
- char str[3] = "hi";
- char str[] = "hi";
- char str[3] = {'h', 'i', '\0'};
字符函数
strcpy 复制
strcat 链接
strcmp 比较
strlen 返回字符串长度
多维数组
二维数组:理解为多个一维数组组成
-
定义
type arr[一维数组个数][每一个一维数组的成员个数]; int arr[2][3];由两个一维数组组成,每一个一维数组中有3个整形变量组成 arr--->int *[3] arr[0]-->int * -
初始化
int arr[2][3] = {{1,2,3}, {4,5,6}}; arr[0] == &arr[0][0]; arr[1] == &arr[1][0]; *arr+4 == &arr[1][1] == arr[1] + 1
#include <stdio.h>
#include <string.h>
int main()
{
int s[2][3] = {{9,8,7}, {3,2,1}}, (*p)[3];
p = s; // s int *[3]
//printf("&p = %p\n", &p);
printf("s = %p\n", s);
printf("p+1 = %p\n", p+1); // 因为 p = s 相当于s的地址加三 到第二行
printf("p = %p\n", p);
printf("&p = %p\n", &p); //p的地址
printf("*p = %p\n", *p); // *p对p的地址取值
printf("**(p+1) = %d\n", **(p+1));
printf("**s = %d\n", **(p+1));
printf("**(p+1)= %d\n", **(p+1));
printf("**p+1= %d\n", **p+1);
printf("*(*s+4) = %d\n", *(*s+4)); // *s+4 == &s[1][1] == sr[1] + 1
printf("*(*p+4) = %d\n", *(*p+4));
printf("**p = %d\n", **p);
}
s = 000000000061FE00
p+1 = 000000000061FE0C
p = 000000000061FE00 // 和s 的地址相等
&p = 000000000061FDF8 // p 本身的地址
*p = 000000000061FE00 //对本身的地址取*操作得到p存放的值
**(p+1) = 3
**s = 3
**(p+1)= 3
**p+1= 10
*(*s+4) = 2
*(*p+4) = 2
**p = 9
指针
存储变量的地址 指针只能存地址!!!
定义
-
type *name;
char *p = NULL; // 变量p 类型char * //如果不确定p的指向暂时赋为 NULL(空)
类型
- 步长 取值大小
- *printf("%p\n", p+1); // char 指针+1---> 加sizeof(char)个字节
- 存地址---》变量大小8字节
#include <stdio.h>
int main(void)
{
char str[] = "hello world";
char *p = str; // 变量p 类型char *
int arr[5] = {1,2,3,4,5};
int *q;
q = arr;
printf("%ld\n", sizeof(p));
printf("%p\n", p);
printf("%p\n", p+1); // char *指针+1--->sizeof(char)
while (*p) {
printf("%c", *p);// p-->char * (*p)-->取
p++;
}
printf("\n");
printf("%ld\n", sizeof(q));
printf("%p\n", q);
printf("%p\n", q+1);
printf("%d\n", *q);
return 0;
}
指针常量和常量指针const
#include <stdio.h>
/*
指针常量
常量指针
*/
int main(void)
{
char str[] = "hello everyone";
// 指针指向的是常量(*p),指针所指向的地址空间内容只读的
const char *p = str; // 常量(的)指针
char const *m = str; // 同上
char *const q = str; // 指针(的)常量
const char *const q = str // *q 和 q 都没改变
str[0] = 'm';
printf("%s\n", str);
// *p = 'h'; 错误 *p 是常量不能改变
p++;
// q++; 错误 q 是常量不能改变
*q = 'h';
printf("%s\n", str);
return 0;
}
用途
- 形参通过得到实参地址---》改变实参
- 可以通过参数返回值---》回填
- 数组,函数可以作为形参
#include <stdio.h>
/*
指针的作用:
1. 通过形参改变实参---》指针
*/
void swap2num1(int a, int b);
void swap2num(int *a, int *b);
int maxMinArr(int *arr, int n, int *min);
int main(void)
{
int num1, num2;
int arr[] = {3,2,7,6,8,9,4};
int max, min;
num1 = 100;
num2 = 50;
swap2num(&num1, &num2);
//swap2num1(num1, num2 );
printf("num1:%d, num2:%d\n", num1, num2);
max = maxMinArr(arr, sizeof(arr) / sizeof(*arr), &min);
printf("最大值:%d, 最小值:%d\n", max, min);
return 0;
}
/*两个整型数的交换*/
void swap2num(int *a, int *b)
{
int t;
t = *a; // t = num1
*a = *b; // num1 = num2
*b = t; // num2 = t
}
void swap2num1(int a, int b)
{
int t;
t = a; // t = num1
a = b; // num1 = num2
b = t; // num2 = t
}
/*
将给定的数组的最大最小值返回
参数的回填
*/
int maxMinArr(int *arr, int n, int *min)
{
int i;
int maxn, minn;
maxn = minn = arr[0];
for (i = 1; i < n; i++) {
if (arr[i] > maxn) {
maxn = arr[i];
}
if (arr[i] < minn)
minn = arr[i];
}
*min = minn;
return maxn;
}
指针操作
#include <stdio.h>
#define MONTHS 12
int main(void)
{
int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
int index;
int *p = days;
//days+1 返回的是整个表达式的值,而 days 还是那个常量 days
printf("Month %2d has %d days.\n", index + 1, *days+1);
//days++; //错误 days 的值不可修改 数组名是常量;
//是要把 days 这个常量自增,即加1,那肯定不行,要报错;
for (index = 0; index < MONTHS; index++)
printf("Month %2d has %d days.\n", index + 1, *(days + index));
return 0;
}
#include <stdio.h>
int main(void)
{
// !!!!!!!!指针只能存地址
char str[] = "good morning";
char *q = str;
char *p;
p = q;
// ++在后先运算在++
printf("%c\n", *p++); // 'g' arg = *p++; 先对*p做运算在对p++ printf()就相当于运算把*p的值打印出来
printf("%c\n", (*p)++); // 'o' arg = (*p)++ "gpod moring" arg==>'o' 先对*p做运算在对(*p)做++ 运算; (*p)++ = (*p)+ 1 = 'o' + 1 = p'
printf("%c\n", *(p++)); // 'p' arg = *(p++) arg = *p; p++ 先取*p 在p++
printf("%c\n", *++p); // 'd' 先++p 在取*
printf("%c\n", ++*p); // 'e' 先++*p ==> *p = 1 + *p 在取*p
puts(str);
puts(p-3);
return 0;
}
#include<stdio.h>
int main(void)
{
int urn[5] = {100, 200, 300, 400, 500};
int *ptr1, *ptr2, *ptr3;
ptr1 = urn;
ptr2 = &urn[2];
printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n",ptr1, *ptr1, &ptr1);
//指针加法
ptr3 = ptr1 + 4;
printf("ptr3 = %p, ptr1 + 4 = %p, *(ptr1 + 4) = %d\n",ptr3, ptr1 +4 , *(ptr1 + 4));
ptr1++; //递增指针
printf("ptr1 = %p, *ptr = %d, &ptr1 = %p\n",ptr1, *ptr1, &ptr1);
ptr2--; //递减指针
printf("ptr2 = %p, *ptr2 = %d, &ptr2 = %d\n",ptr2, *ptr2, &ptr2);
--ptr1; //恢复初始值
++ptr2; //恢复初始值
printf("ptr1 = %p, ptr2 = %p\n",ptr1, ptr2);
// 一个指针减去另一个指针
printf("ptr2 = %p, ptr1 = %p, ptr2 - ptr1 = %td\n",ptr2, ptr1, ptr2 - ptr1);
//一个指针减去一个整数
printf("ptr3 = %p ,ptr3 - 2 = %p\n", ptr3, ptr3 - 2);
}
ptr1 = 000000000061FE00, *ptr1 = 100, &ptr1 = 000000000061FDF8
ptr3 = 000000000061FE10, ptr1 + 4 = 000000000061FE10, *(ptr1 + 4) = 500
ptr1 = 000000000061FE04, *ptr = 200, &ptr1 = 000000000061FDF8
ptr2 = 000000000061FE04, *ptr2 = 200, &ptr2 = 6422000
ptr1 = 000000000061FE00, ptr2 = 000000000061FE08
ptr2 = 000000000061FE08, ptr1 = 000000000061FE00, ptr2 - ptr1 = 2
ptr3 = 000000000061FE10 ,ptr3 - 2 = 000000000061FE08
二级指针
1. 存储一级指针变量的地址
2. 用途
1. 存储指针数组首地址
2. 参数列表返回地址(堆)
数组指针
-
int (*p)[3]; 存储的3个地址连续整型数的地址
-
#include <stdio.h> int main(void) { int arr[2][3] = {1,2,3,4,5,6}; // arr--->arr[0] arr[1] // arr[0]--->{1,2,3} // arr--->&arr[0]--->int *[3] int (*p)[3]= arr; // 数组指针 printf("%p\n", p); printf("%p\n", p+1); printf("%d\n", p[0][2]); return 0; }
指针数组
#include <stdio.h>
int main(int argc, char *argv[]/* char **argv */)
{
#if 0
// 指针数组
char *arr[4] = {"hello", "world", "good", "afternoon"};
for (int i = 0; i < 4; i ++) {
printf("%s\n", arr[i]);
}
// arr--->char **
char **p = arr; // 二级指针
#endif
for (int i = 0; i < argc; i++) {
puts(argv[i]);
}
return 0;
}
动态开辟
- 堆空间使用
malloc(3)
calloc(3)
reallock(3)
free(3);
void *万能指针类型
void 指针可以存储任意类型的变量地址,但没有步长,不能+- 也不能
memset(3); 存储空间初始化
memcpy(3); 存储空间复制
memmove(3); 功能同上
函数指针
int (*p)(int , int)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void sortarr(void *arr, int nmemb, int size, int (*cmp)(const void *data1, const void *data2));
static int intcmp(const void *data1, const void *data2)
{
const int *d1 = data1;
const int *d2 = data2;
return *d2 - *d1;
}
static int charcmp(const void *data1, const void *data2)
{
const char *c1 = data1;
const char *c2 = data2;
return *c1 - *c2;
}
static int stringcmp(const void *data1, const void *data2)
{
char *const*d1 = data1;
char *const*d2 = data2;
return strcmp(*d2, *d1);
}
int main(void)
{
int arr[] = {3,2,1,6,7};
char str[] = "hello world";
char *sarr[] = {"hello", "world", "hi", "good", "girls", "boys"};
double strrr[] = {4.33,5.66,7.00,9.66};
sortarr(strrr,sizeof(strrr) / sizeof(*strrr),sizeof(double),intcmp);
sortarr(arr, sizeof(arr) / sizeof(*arr), sizeof(int), intcmp);
sortarr(str, strlen(str), sizeof(char), charcmp);
sortarr(sarr, sizeof(sarr) / sizeof(*sarr), sizeof(char *), \
stringcmp);
for (int i = 0; i < sizeof(strrr) / sizeof(*strrr); i++)
printf("%d ", strrr[i]);
printf("\n");
for (int i = 0; i < sizeof(arr) / sizeof(*arr); i++)
printf("%d ", arr[i]);
printf("\n");
printf("%s\n", str);
for (int i = 0; i < sizeof(sarr) / sizeof(*sarr); i++)
printf("%s\n", sarr[i]);
return 0;
}
// 任意类型的数组排序
// (char *)arr + size*i
void sortarr(void *arr, int nmemb, int size, int (*cmp)(const void *data1, const void *data2))
{
int i, j;
char *tmp;
tmp = malloc(size);
if (NULL == tmp)
return;
for (i = 0; i < nmemb-1; i++) {
for (j = 0; j < nmemb-i-1; j++) {
if (cmp((char *)arr+j*size, (char *)arr+(j+1)*size) < 0) {
memcpy(tmp, (char *)arr+j*size, size);
memcpy((char *)arr+j*size, (char *)arr+(j+1)*size, size);
memcpy((char *)arr+(j+1)*size, tmp, size);
}
}
}
free(tmp);
}

浙公网安备 33010602011771号