C语言:大伟笔记202305
变量作用域(scope)
-
代码块作用域 :{}
-
文件作用域 :写的地方到文件结束
-
原型作用域 :函数原型声明的时候的变量名可以随便 写
-
函数作用域 :配合goto语句的标签
链接属性
编译器编译c源码和基础库做链接,生成可执行程序。
- external 可以去别的文件里面去找具有文件作用域的对象
- internal 用static关键字来限制external对象,限制后只能本文件内访问
- none 代码块变量是空链接属性
生存期
- 静态存储期 (static storage duration):文件作用域,程序退出释放。
- 自动存储期 (automatic storage duration): 代码块作用域,代码块结束时效
变量存储类型
- auto :自动存储区,代码块中的变量默认是atuo类型
- register : 自动存储区,寄存器变量是有可能存储在cpu内部的
- static : 静态存储区,静态局部变量的生存期和全局变量一样但是作用域还是块
- extern : 声明外部再找找
- typedef : 对类型的封装,给类型取别名,配合结构体使用更方便
关键字struct
#include <stdio.h>
#include <stdlib.h>
struct Date
{
int year;
int mouth;
int day;
};
int main(void)
{
struct Date *date;
date = (struct Date *)malloc(sizeof(struct Date));
if (date == NULL)
{
printf("内存分配失败\n");
exit(1);
}
date->year = 2023;
date->mouth = 5;
date->day = 20;
printf("%d-%d-%d", date->year, date->mouth, date->day);
return 0;
}
关键字typedef
#include <stdio.h>
#include <stdlib.h>
typedef struct Date
{
int year;
int mouth;
int day;
} DATE, *P_DATE;
int main(void)
{
P_DATE date;
date = (P_DATE)malloc(sizeof(DATE));
if (date == NULL)
{
printf("内存分配失败\n");
exit(1);
}
date->year = 2023;
date->mouth = 5;
date->day = 20;
printf("%d-%d-%d", date->year, date->mouth, date->day);
return 0;
}
指针函数和函数指针
#include <stdio.h>
/*
函数指针 int (*fp)(int, int)
函数指针作入参
函数指针作返回值
*/
int add(int, int);
int sub(int, int);
int compt(int (*)(int,int),int,int);
int (*select(char))(int, int);
int add(int a,int b){
return a+b;
}
int sub(int a,int b){
return a-b;
}
// 函数指针作入参的例子
int compt(int (*fp)(int,int),int a,int b){
return (*fp)(a,b);
}
// 函数指针作返回值的例子
int (*select(char o))(int a,int b){
switch (o)
{
case '+':
return add;
case '-':
return sub;
}
}
int main(){
int rest;
// 函数指针作入参的例子
rest = compt(add,1,2);
printf("a+b = %d\n",rest);
rest = compt(sub,1,2);
printf("a-b = %d\n",rest);
// 函数指针作返回值的例子
char o;
int a,b;
int (*fp)(int, int);
printf("请输入计算式(例如2+3):");
scanf("%d%c%d",&a,&o,&b);
fp = select(o);
rest = compt(fp,a,b);
printf("rest = %d\n",rest);
}
递归
函数调用它本身。递归必须要有结束条件,否则程序会崩溃。
#include <stdio.h>
void func01(int);
void func01(int n)
{
printf("%d aaaa\n",n);
if (--n)
{
func01(n);
}
}
int main(void)
{
func01(3);
func01(3);
}
#include <stdio.h>
long func01(int);
long func01(int n)
{
long m;
if (n > 0)
{
m = n * func01(n-1);
}
else
{
m = 1;
}
return m;
}
int main(void)
{
long int a = func01(5);
printf("%d\n", a);
long int b = func01(5);
printf("%d\n", b);
}
快速排序
快速排序算法的基本思想是:通过一趟排序将待排序数据分割成独立的两个部分,其中一部分的所有元素均比另一部分的元素小,然后分别对这两部分继续进行排序,重复上述步骤直到排序完成。
#include <stdio.h>
void print_list(int *int01, int length)
{
int i;
for (i = 0; i < length; i++)
{
printf("%d ", *int01++);
}
printf("\n");
}
void quick_sort(int array[], int left, int right)
{
int i = left, j = right;
int temp;
int pivot;
pivot = array[(left + right) / 2];
while (i < j)
{
// 从左到右找到大于等于基准点的元素
while (array[i] < pivot)
{
i++;
}
// 从右到左找到小于等于基准点的元素
while (array[j] > pivot)
{
j--;
}
printf("currnt: %d,%d\n", i, j);
// 如果i<=j,则互换
if (i <= j)
{
temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
}
print_list(&array[0], 9);
if (left < j)
{
printf("\n sort right array[%d,%d]\n", left, j);
quick_sort(array, left, j);
}
if (i < right)
{
printf("\n sort left array[%d,%d]\n", i, right);
quick_sort(array, i, right);
}
}
int main(void)
{
int array[] = {9, 1, 8, 3, 7, 2, 6, 5, 4};
int i, length;
length = sizeof(array) / sizeof(array[0]);
printf("\n sort all array[%d,%d]\n", 0, length-1);
quick_sort(array, 0, length - 1);
print_list(&array[0], length);
return 0;
}
内存管理函数
-
malloc 申请动态内存空间 viod *malloc(size_t size); 在堆上,需要手动释放。
-
free 释放动态内存空间 void free(void *ptr);释放堆上的内存
-
calloc 申请并初始化一系列内存空间 void *calloc(size_t nmemb, size_t size);初始为0。
-
realloc 重新分配内存空间 viod *realloc(void *ptr, size_t size);重新分配并拷贝。
(注明: 普通的局部变量在栈上。)
-
memset 使用一个常量字节填充内存空间 memset(ptr, 0, N * sizeof(int));
-
memcpy 拷贝内存空间 memcpy(prt2,prt1,N); free(ptr1);
-
memmove 拷贝内存空间
-
memcmp 比较内存空间
-
memchr 在内存空间中搜索一个字符
根据用户输入动态扩容内存的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int num, i;
int count = 0;
int *ptr = NULL;
do
{
printf("input number: ");
scanf("%d", &num);
count++;
ptr = (int *)realloc(ptr, count * sizeof(int));
if (ptr == NULL)
{
exit(1);
}
ptr[count - 1] = num;
} while (num != -1);
for (i = 0; i < count; i++)
{
printf("%d ", ptr[i]);
}
putchar('\n');
free(ptr);
return 0;
}
内存布局
#include <stdio.h>
#include <stdlib.h>
int global_uninit_var;
int global_init_var1 = 520 ;
int global_init_var2 = 880 ;
void func(void);
void func(void){
;
}
int main(void){
int local_var1;
int local_var2;
static int static_uninit_var;
static int static_init_var = 456;
char *str1 = "1111"; //字符串常量
char *str2 = "2222";
char str3[5] = {'a','b','1','2','\0'}; //局部变量
char str4[5] = {'a','b','1','2','\0'};
int *malloc_var = (int *)malloc(sizeof(int));
printf("addr of local_var1 -> %p\n",&local_var1);
printf("addr of local_var2 -> %p\n",&local_var2);
printf("addr of str3 -> %p\n",str3);
printf("addr of str4 -> %p\n",str4);
printf("addr of malloc_var -> %p\n",malloc_var);
printf("addr of global_uninit_var -> %p\n",&global_uninit_var);
printf("addr of static_uninit_var -> %p\n",&static_uninit_var);
printf("addr of static_init_var -> %p\n",&static_init_var);
printf("addr of global_init_var2 -> %p\n",&global_init_var2);
printf("addr of global_init_var1 -> %p\n",&global_init_var1);
printf("addr of str2 -> %p\n",str2);
printf("addr of str1 -> %p\n",str1);
printf("addr of func -> %p\n",func);
return 0;
}
[root@wei07 c]# gcc t.c -o t && ./t
addr of local_var1 -> 0x7ffc0d64f00c
addr of local_var2 -> 0x7ffc0d64f008
addr of str3 -> 0x7ffc0d64f000
addr of str4 -> 0x7ffc0d64eff0
addr of malloc_var2 -> 0x1d32030
addr of malloc_var1 -> 0x1d32010
addr of global_uninit_var -> 0x601050
addr of static_uninit_var -> 0x60104c
addr of static_init_var -> 0x601044
addr of global_init_var2 -> 0x601040
addr of global_init_var1 -> 0x60103c
addr of str2 -> 0x4007a5
addr of str1 -> 0x4007a0
addr of func -> 0x40057d
[root@wei07 c]#
变量名称 | 变量类型 | 内存地址 | 内存段 |
---|---|---|---|
local_var1 | 局部变量 | 高(大) | 栈stack |
local_var2 | 局部变量 | 栈stack | |
str3[] | 局部变量 | 栈stack | |
str4[] | 局部变量 | 栈stack | |
malloc_var2 | 动态申请的内存空间 | 堆heap | |
malloc_var1 | 动态申请的内存空间 | 堆heap | |
global_uninit_var | 全局变量(未初始化) | BSS | |
static_uninit_var | 静态变量(未初始化) | BSS | |
static_init_var | 静态变量(初始化) | 数据段 | |
global_init_var2 | 全局变量(初始化) | 数据段 | |
global_init_var1 | 全局变量(初始化) | 数据段 | |
str2 | 字符串常量 | 代码段 | |
str1 | 字符串常量 | 代码段 | |
func | 函数 | 低(小) | 代码段 |
注明1:BSS是英文Block Started by Symbol的简称,这个区段的数据在程序运行前将被自动初始化为数字0.
注明2:linux命令size,显示程序的text,data,bss段的大小。程序的本质就是这3个段。
注明3:堆和栈的区别 ( 堆由程序员手动申请,栈由系统自动分配;堆到主动释放时结束,栈到局部函数返回时结束;不同函数可以自由访问堆,不同函数不能相互访问栈;堆由低往高用,栈由高往低用,他们共享一片蓝天。)
D:\soft\src\Python3\C>gcc t.c -o t && t
addr of local_var1 -> 000000000061FDFC
addr of local_var2 -> 000000000061FDF8
addr of str3 -> 000000000061FDF3
addr of str4 -> 000000000061FDEE
addr of malloc_var2 -> 0000000000736BD0
addr of malloc_var1 -> 0000000000736BB0
addr of global_uninit_var -> 0000000000407970
addr of static_uninit_var -> 0000000000407030
addr of static_init_var -> 0000000000403018
addr of global_init_var2 -> 0000000000403014
addr of global_init_var1 -> 0000000000403010
addr of str2 -> 0000000000404005
addr of str1 -> 0000000000404000
addr of func -> 0000000000401550
D:\soft\src\Python3\C>
UNIX接口-低级IO-read和write
#include "stdio.h"
#include "unistd.h"
#define BUF 6
void copy01();
int getchar01();
int getchar02();
int main()
{
copy01();
char c;
c = getchar01();
printf("%c\n", c);
c = getchar01(); // 等待read
printf("%c\n", c);
c = getchar02();
printf("%c\n", c);
c = getchar02();
c = getchar02();
c = getchar02(); // 要么从缓存里面取,要么等待read
c = getchar02();
c = getchar02();
c = getchar02();
printf("%c\n", c);
// 注明:
// 如果把getchar02放到01前面执行,02会一次性尽量读取BUFSIZE512个字符,
// 会发现01要等待第二次的标准输入。
return 0;
}
/* 复制标准输入到标准输出 */
void copy01(void)
{
char exit0 = '0';
char buf[BUF];
int n;
printf("请输入内容,回车结束。输入0时,退出复制。\n");
while ((n = read(0, buf, BUF)) > 0)
{
if (buf[0] == exit0)
{
printf("退出。\n");
break;
}
write(1, buf, n);
}
}
/* 不带缓冲的getchar */
int getchar01(void)
{
char c;
return (read(0, &c, 1) == 1) ? (unsigned char)c : EOF;
}
/* 带缓冲的getchar */
int getchar02(void)
{
static char buf[BUFSIZ];
static char *bufp = buf;
static int n = 0;
if (n == 0)
{
n = read(0, buf, sizeof(buf));
bufp = buf;
}
return (--n >= 0) ? (unsigned char)*bufp++ : EOF;
}
共用体union
特殊的结构体,里面的所有成员共用同一个地址,同一时间只能用里面的一个成员。
union Test{
int t;
double pi;
char str[6];
};
时间tm结构
#include <time.h>
int main(void){
strcut tm *p;
time_t t;
time(&t); //秒数赋值
p = localtime(&t); //tm结构体赋值
return 0;
}
struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月,范围从 0 到 11 */
int tm_year; /* 自 1900 年起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
};
枚举类型enum
如果一个变量有且仅有几种值,那么就适合用枚举类型。
int main(void){
enum Week {sun,mon,tue,wed,thu,fri,sat};
enum Week today;
strunt tm *p;
time_t t;
time(&t);
p=localtime(&t);
today=p->tm_wday;
return today;
}
注明:枚举就是符号常量,默认0,1,2,3…; 可以定义枚举类型的时候修改默认值,修改后呈现两面派:0,1,12,13…
位域(:节约空间)
位域是一个成员站用几个2进制位。
struct Test{
unsigned int a:1;
unsigned int b:1;
unsigned int c:2;
};
位运算
-
~ 按位取反
-
& 按位与
-
^ 按位非
-
| 按位或
注明:配合掩码使用很好。
移位运算
value << 2 ; 左移2位
value >> 2 ; 右移2位
打开和关闭文件
fp = fopen("aaa.txt","r");
fclose(fp);
参考网站: https://fishc.com.cn/thread-90808-1-1.html
// 函数原型
#include <stdio.h>
...
FILE *fopen(const char *path, const char *mode);
参数解析:
参数 | 含义 |
---|---|
path | 该参数是一个 C 语言字符串,指定了待打开的文件路径和文件名 |
mode | “r” …如下 |
“r” 1. 以只读的模式打开一个文本文件,从文件头开始读取 2. 该文本文件必须存在
"w"1. 以只写的模式打开一个文本文件,从文件头开始写入 2. 如果文件不存在则创建一个新的文件 3. 如果文件已存在则将文件的长度截断为 0(重新写入的内容将覆盖原有的所有内容)
"a"1. 以追加的模式打开一个文本文件,从文件末尾追加内容 2. 如果文件不存在则创建一个新的文件
"r+"1. 以读和写的模式打开一个文本文件,从文件头开始读取和写入 2. 该文件必须存在 3. 该模式不会将文件的长度截断为 0(只覆盖重新写入的内容,原有的内容保留)
"w+"1. 以读和写的模式打开一个文本文件,从文件头开始读取和写入 2. 如果文件不存在则创建一个新的文件 3. 如果文件已存在则将文件的长度截断为 0(重新写入的内容将覆盖原有的所有内容)
"a+"1. 以读和追加的模式打开一个文本文件 2. 如果文件不存在则创建一个新的文件 3. 读取是从文件头开始,而写入则是在文件末尾追加
"b"1. 与上面 6 中模式均可结合(“rb”, “wb”, “ab”, “r+b”, “w+b”, “a+b”) 2. 其描述的含义一样,只不过操作的对象是二进制文件
返回值:
-
如果文件打开成功,则返回一个指向 FILE 结构的文件指针;
-
如果文件打开失败,则返回 NULL 并设置 errno 为指定的错误。
顺序读写文件
读写单个字符
int __cdecl fgetc(FILE *_File);
int __cdecl fputc(int _Ch,FILE *_File);
读写字符串
char *__cdecl fgets(char * __restrict__ _Buf,int _MaxCount,FILE * __restrict__ _File);
int __cdecl fputs(const char * __restrict__ _Str,FILE * __restrict__ _File);
注明1:_MaxCount是字符数量+1,因为末尾自动添加’\0’再返回。
注明2:如果没有到达_MaxCount就遇到了’\n’,则只读取到’\n’。
格式化读写文件
int __cdecl fprintf(FILE * __restrict__ _File,const char * __restrict__ _Format,...);
int __cdecl fscanf(FILE * __restrict__ _File,const char * __restrict__ _Format,...) __MINGW_ATTRIB_DEPRECATED_SEC_WARN;
指定尺寸读写文件
size_t __cdecl fread(void * __restrict__ _DstBuf,size_t _ElementSize,size_t _Count,FILE * __restrict__ _File);
size_t __cdecl fwrite(const void * __restrict__ _Str,size_t _Size,size_t _Count,FILE * __restrict__ _File);
随机读写文件
-
获取位置指示器的位置
long __cdecl ftell(FILE *_File);
-
设置位置指示器的位置
int __cdecl fseek(FILE *_File,long _Offset,int _Origin);
#define SEEK_CUR 1 #define SEEK_END 2 #define SEEK_SET 0
错误号打印
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(void){
FILE *fp;
if ((fp = fopen("aaa.txt","r")) == NULL){
perror("打开文件失败");
fprintf(stderr,"错误:%s \n打开文件失败。\n",strerror(errno));
exit(EXIT_FAILURE);
}
fclose(fp);
return 0;
}
注明: perror 和 strerror(errno) ,二选一。
IO缓冲区
- 手动刷新缓冲区: fflush
- 设置缓冲区参数: setvbuf
- 按块缓存 _IOFBF
- 按行缓存 _IOLBF
- 不缓存 _IONBF
int __cdecl fflush(FILE *_File);
int __cdecl setvbuf(FILE * __restrict__ _File,char * __restrict__ _Buf,int _Mode,size_t _Size);