C基础,零散内容

C语言库的制作方法

1:把一类功能的函数写到一个xxx.c里面

2:把xxx.c中的所有函数的声明写到xxx.h中去

3:避免头文件重复语句

#ifndef __MYSTRING_H__
#define __MYSTRING_H__
这里写函数声明
#endif

4:将xxx.h包含到xxx.c中去 (自包含)

#include "mystring.h"   //系统用<>自实现用“”

5:在main函数中 包含 xxx.h 谁用谁包含

main参数

int main(int argc,char *argv[])
//argc  arfument 参数  c  count  个数
//argv          指针数  v  vector 向量(数组可以理解为向量)
{
}
argv【】内保存着传入main函数的字符串的地址
这些参数在内存中存放才 栈以上的命令行参数的位置

auto关键字

描述:这个这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。auto关键字在我们写的代码里几乎看不到,但是它又无处不在,它是如此的重要,又是如此的与世无争,默默的履行着自己的义务,却又隐姓埋名。

**作用:**C程序是面向过程的,在C代码中会出现大量的函数模块,每个函数都有其生命周期(也称作用域),在函数生命周期中声明的变量通常叫做局部变量,也叫自动变量。例如:

int fun(){
int a = 10; // auto int a = 10;
// do something
return 0;
}
int fun(){
int a = 10; // auto int a = 10;
// do something
return 0;
}

整型变量a在fun函数内声明,其作用域为fun函数内,出来fun函数,不能被引用,a变量为自动变量。也就是说编译器会有int a = 10之前会加上auto的关键字。

auto的出现意味着,当前变量的作用域为当前函数或代码段的局部变量,意味着当前变量会在内存栈上进行分配。

栈空间:

在Linux中命令 ulimit -a 可以查看栈的大小

aa@aa-virtual-machine:~$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15409
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15409
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

可以看到只能空间大小为 8192kb,这并不大,这说明了–>栈适用于数据交换的而不适合是用于大数据存储,堆空间主要用于大内存的申请

memset()函数

用于对数组和对空间的初始化,针对的单位是字节

memset(指向要初始化的指针,初始化的内容,初始化的大小)

memset针对的字节的初始化
memset(pa,1,10*sizeo(int))
那么在内存中式这样的(32位机器)
0x0101010101010101 这是一个int大小的内存所初始化的数据。

calloc函数

函数声明:void* calloc(size_t nmemb,size_t size)

所在文件: #include<stdlib.h>

函数功能:申请对空间并返回所申请的空间,并且自动清零

参数:size_t nmemb 所需内存单元的数量(单位式字节)

​ size_t size 内存单元的大小

int *p=(int*)calloc(10,sizeof(int))
//申请10的大小为int的内存单元

realloc()函数

函数声明 void *realloc(void *ptr,size_t size)

所在文件: stdlib.h

函数功能:扩容或缩小原有内存的大小,常用于扩容,缩小会导致内存缩去的部分空间数据丢失

返回值:返回空间的起始位置

需要注意的是:当使用realloc是如果后面的连续空间并没有所要申请的大小那么realloc就会重新找一个可以放下原先自己的内存和还要申请的内存的空间,这可能导致原来指针的变化,所以有以下模板:

char *pa=(char*)malloc(0x10);
char *newpa=Null;
newpa=realloc(pa,0x10)

当然有更好的办法

char *pa=(char*)maloc(0x10);
pa=realloc(pa,0x10);

关于构造类型

对数组类型起别名:

typedef int ARRAY[10]  //给一个大小为10字节,内部存放int型的变量取别名为ARRAY

具体的起别名方法:

int arr[10]  //先写出要定义的类型
dypedef int arr[10]  //在前部加上typedef
dypedef in ARRAY[10]  //将名字换成想要定义的名字,完成操作

int a              //第一步
typedef int a	   //第二步
typedef int int64  //第三步

char *p
typedef char *p
typedef char* pstr

结构体就不用说了,太简单

typedef 构成的C语言语句在编译是起作用

#define 所写的为宏定义,在预处理阶段起作用

int arr={1,2,3,4,5,6}  //这是对数组类型的初始化
arr={2,3,4,5,6,7}      //这是错误的

struct stu
{
	char *name;
	char sex;
	float score;
};
struct stu s={"zhangsan",'f',99.9}; //这是对结构体的初始化
s={"zhgnsan","w",88.8};  //这是错误的

凡是构造类型,要么在定义的时候初始化,不可以先定义再以初始化的方式赋值

相同结构体之间可以进行赋值运算,此语法基础奠定了可以用于传参和反值

typedef struct stu
{
char name[100];
char sex[20];
};
stu s1,s2;
s2={"zhangsan",nan};
s1=s2;   //赋值运算,那么s1内的成员的值将于s2相同

结构体函数

#include<stdio.h>
typedef struct mycomplex
{
	float real;
	float image;
}mycomplex;
//函数实现结构体的运算,并返回结构体
mycomplex mycomplexadd(mycomplex s1,mycomplex s2)
{
complex t;
t.real=s1.real+s2.real;
t.image=s1.image+s2.image;
return t
}
int main()
{
	
}

结构体内嵌套结构体

#include<stdio.h>
int main()
{
    typedef struct demol
    {
        char name[10];
        int score;
        char sex;
    struct birth
    {
        int year;
        int moth;
        int day;
    }birth;     //记得要分配内存

    }tru;   


tru s={"wangwu",99,'f',{1999,9,9}};
printf("year=%d",s.birth.year);
return 0;
}

结构体类型的大小

涉及内存对齐

typedef struct _staff
{
char sex;
int age;
short num;
}Staff;    //这只是一个类型而不是变量,所以不占空间

int main(int argc,char *argv[])
{
	Staff s;
	printf("sizeof(Staff) = %d\n",sizeof(Staff));
	printf("sizeof(s) = %d\n",sizeof(s));
	printf("&s = %p\n",&s);
	printf("&s.sex = %p\n",&s.sex);
	printf("&s.age = %p\n",&s.age);
	printf("&s.num = %p\n",&s.num);
	return 0;

}
终端输出:
sizeof(Staff) = 12
sizeof(s) = 12
&s = 000000000061FE14
&s.sex = 000000000061FE14      
&s.age = 000000000061FE18      
&s.num = 000000000061FE1C
//为什么sex后空了3个字节在太难age
//为什么num后面空了2个字节
//还有第三个
    
typedef struct _staff
{
char sex;
short num;   //调换了num和age的顺序
int age;

}Staff;    //这只是一个类型而不是变量,所以不占空间

int main(int argc,char *argv[])
{
	Staff s;
	printf("sizeof(Staff) = %d\n",sizeof(Staff));
	printf("sizeof(s) = %d\n",sizeof(s));
	printf("&s = %p\n",&s);
	printf("&s.sex = %p\n",&s.sex);
	printf("&s.age = %p\n",&s.age);
	printf("&s.num = %p\n",&s.num);
	return 0;

}

终端输出:
sizeof(Staff) = 8
sizeof(s) = 8
&s = 000000000061FE18
&s.sex = 000000000061FE18      
&s.age = 000000000061FE1C      
&s.num = 000000000061FE1A
    
    
还没完
#include<stdio.h>
#pragma pack(1)
typedef struct _staff
{
char sex;
short num;   //调换了num和age的顺序
int age;

}Staff;    //这只是一个类型而不是变量,所以不占空间

int main(int argc,char *argv[])
{
	Staff s;
	printf("sizeof(Staff) = %d\n",sizeof(Staff));
	printf("sizeof(s) = %d\n",sizeof(s));
	printf("&s = %p\n",&s);
	printf("&s.sex = %p\n",&s.sex);
	printf("&s.age = %p\n",&s.age);
	printf("&s.num = %p\n",&s.num);
	return 0;
}

终端输出
sizeof(Staff) = 7
sizeof(s) = 7
&s = 000000000061FE19
&s.sex = 000000000061FE19
&s.age = 000000000061FE1C
&s.num = 000000000061FE1A        

内存对齐:

什么是内存不对齐

一个成员变量需要多个机器周期去读的现象,称为内存不对齐。,机器周期是指机器一次读取的位数,如32位机器一次读32位也就是4个字节,就那上面的列子来解释:

char sex; 本身占一个字节

int age; 本身占4个字节

用 。 来表示字节数

内存中

。 。 。 。 。 。 。 。

char int

上面是内存对齐,因为机器一次读取4字节数据char和int都只需要读取一次就可以读到

。 。 。 。 。 。

char int

上面是内存不对齐,因为机器在读取 int 时第一次读取4字节后三个字节是有用的,但没有读完整个 int 所以就需要第二次读写,第二次读4个字节只有第一个字节是有用的,最后需要将这两个字节拼接整合成一个int

为什么要内存对齐

本质是牺牲空间换取时间

对齐规则

x86(linux 默认 #pragma pack(4)),windows 默认progam(8)。Linux做大支持4字节对齐

方法:

  • 取pack(n)的值 n(n=1 2 4 8…),取结构体中类型最大的值 m,两者取小即为外对齐,大小为 Y

  • 将每一个结构体的成员与Y比较取较小者为 x,作为内对奇的大小

  • 所谓按 x对齐,即为地址(其实地址为0),能被 x整除的地方开始存放数据

  • 外对齐原则是依据 Y的值(Y的最小整数倍),进行补空操作

结构体中使用指针注意
#include<stdio.h>
typedef struct _stu
{
	char *name;
	int score;
}Stu;
int main(int argc,char *argv[])
{
	Stu *ps=malloc(sizeof(Stu));
	ps->name=(char *)malloc(100);
	strcpy(ps->name,"jimGreen"); //使用strcpy的前提是要满足该地址有足够的空间,如果没有上面的那句,那么程序将崩溃
	ps->score=100;
	
	//使用完成后要释放内存
	free(ps->name)
	free(ps) //申请空间的时候从外至内,释放空间的时候从内之外
	
}

链表

尾插法

//每次插入都将 t 指向最后一个节点
typedef struct node
{
    /* data */
    int data;
    struct node* next;
}Node;

Node * creatList() //尾插法函数
{
    Node *head=(Node*)malloc(sizeof(Node));//分配内存
    Node *cur =head;//用于创建
    Node *t = head; //用于记录

    int stop=1;
    while(stop)
    {
    int nodedata;
    scanf("%d",&nodedata);
    
    if(nodedata==0)
    stop=0;

    cur=(Node *)malloc(sizeof(Node)); //为新的节点申请空间
    t->next=cur; //链接这两个空间
    t=cur;   //移动指针t为保存链表尾部
    }
    t->next =Null;
    
    return head; //返回头节点以便找到此链表
}

头插法

//让新来的节点有所指向,避免打断原来的指向
typedef struct node
{
    /* data */
    int data;
    struct node* next;
}Node;

Node * creatList() //头插法函数
{
    Node *head=(Node*)malloc(sizeof(Node));//分配内存
    
    Node *cur = head;
    int data;
    while(data)
    {
        scanf("%d",&data);
        cur = (Node*)malloc(sizeof(Node));

        cur->next = head->next;  //让新来的节点有所指向
        head->next = cur;   
        cur->data = data;
    }
    
    return head; //返回头节点以便找到此链表
}

头插法要时刻有指针指向head->next,尾插法要时刻有指针指向链表尾部

插入操作

本质就是头插法,尾插法的话首先要到链表的尾部这耗时严重

删除操作,经过优化的
void deleteNode0fList(Node*head,Node *pfind)//pind是要删除的地址
if(pfind->next == Null);
{
	while(head->next != pfind)
		head = head->next;
	free(head->next);
}
else  
//这是用要删除块的下一个块来覆盖要删除的块,然后只要删除要删除的块的下一个块即可
{
	Node *t = pfind;	
	pfind->data = pfind->next->data;
    pfind->next = pfind->next->next;
    free(t);
}
排序

链表的比较:相邻两个块比较,较容易,类似冒泡排序

交换两个数的帅气写法
arr[j]=arr[j]^arr[j+1];
arr[j+1]=arr[j+1]^arr[j];
arr[j]=arr[j]^arr[j+1];
//arr[j]^arr[j+1](1)^arr[j+1](1)^arr[j]^arr[j+1]
排序
void popSortList(Node *head)
{
	int len=lenList(head); 
	for(int i=0;<len-1;i++)
	{
		for(int j=0;j<len-i-1;j++)
		{
			head=head->next;
			if(head->data > head->next->data)
			{
				head=head->next;
				
				head ^=head->next;
				head->next ^= head;
				head ^= head->next;
			}
		}
	}
}
更快的排序
void popSortList(Node *head)
{
	int len=Lenlist(head);
	Node * prep,*p,*q,*t;
	prep = head;
	p = prep->next;
	q = p->next;
	
	for(int i = 0;i<len - 1;i++)
	{
		for(int j = 0;j < len-i-1;;j++ )
		{   
            if(p->data >q->data)
            {
            	prep->next = q;
            	p->next = q->next;
            	q->next =p;
            	
            	t=p;
            	p=q;
            	q=t;
            }
        prep = prep->next;
        p = p->next;
        q = q->next;
		
		}
	}
}
将链表反转

类似于头插法

void reverseLise(Node *head)
{
	Node *cur = head->next;
	head->next = Null;   //切断两个链表
	
	Node *t;			// t指向要链接的快
	while(cur)  //当cur不为空是循环
	{
		t = cur;       
		cur = cur->next;         
		t->next = head->next;     //头插法
		head->next = t;        
	}
}

文件操作

文件流的概念

C语言把文件看作是一个字符的序列,即文件是由一个个的字符流,因此C语言将文件也成为文件流。即当独缺一个文件时,可以不关心文件的可是或结构

文件的分类

文本文件:以ASCII吗存放,一个字符一个字符的存放。文本文件的每一个字节存放ASCII码,代表一个字符。便于对字符的逐个处理,但占用的储存空间较多,而且要花费时间转化

二进制文件:以值(补码)编码格式存放。二进制文件时把数据以二进制的格式存放在文件中,占用空间少。数据按内存中的储存形式存放

FILE结构体:

#include <stdio.h>
int main(int argc,char *argv[]);
{
//通过fopen打开一个文件就将这个文件加载到了内存中(不一定能全部加载完),返回一个FILE *指针
	FILE *pf = fopen("data.txt","w");
	return 0;

}
typedef struct{

	short level;     //缓冲区满空的标志
	unsignde flags; //文件状态标志
	char fd;        //文件描述符
	unsigned char hole;//若无缓冲区不读取字符
	short bsize;    //缓冲区大小
	unsigned char *buffer;//数据传送缓冲区位置
	unsigned char *curp;  //当前读写位置
	unsigned istemp;      //临时文件指示
	short token;          //用作无效检查
}FILE;

当开始执行程序的时候,将自动打开3个文件和相关的流:标准输入流(stdin),标准输出流(stdout),标准错误流(stderr),他们都是FILE*型的指针。流提供了文件和程序的通讯通道

相关函数

fopen()函数

函数声明:

File *open(const char* file name,const char* mode)

mode:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FbjF9LEF-1649308579954)(E:\博客\photo\14-4FILE与fopen.pcwlenv_视频截图_870.jpg)]

r 以只读的形式打开,如果文件不存在,则报错,文件不可写入

w 如果文件不存在,则创建,如果文件存在,则清空,文件不可读

a 如果文件不存在则创建,存在不会清空,文件可写,写入的会追加到尾部,文件不可读

如果是二进制文件,则还要加上 b 比如 rb,r+b等。unix/linux不区分文本文件和二进制文件

fclose()函数

fclose(文件指针变量)

功能:释放此文件指针指向的结构体,刷新缓存,关闭文件

fputc()函数

函数声明:int fputc(char ch,FILE *stream)

功能:将 ch 字符写入文件流中

返回值:写入成功返回写入的字符数,如果失败返回EOF

fgetc()函数

C 库函数 int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。

声明:int fgetc(FILE *stream)

参数:stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要在上面执行操作的流。

返回值:该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF

用法

常常用循环来去文件中的字符

while((int ch = fputc(fp)) != NULL) //赋值运算优先级低于关系运算
{
	putch(ch);
}
feof()函数

函数声明: int feof (FILE * steam)

功能:判断文件是否读到文件的结尾

返回值:判断文件是读到了文件结尾 0 表示未读到结尾 1表示读到结尾

少用

fputs()函数

声明:int fputs(char *str,FILE *fp)

功能:将 str 指向的字符串写入 fp 所指向的文件中,遇到\0 结束

返回值:正常返回 0 ;出错返回 EOF

fgets()函数

声明:char *fgets(char *str, int n, FILE *stream)

功能:将字符串 str中的n个写入文件流stream中

注意:遇到 \n 读取结束并连 \n 一并读出

​ 在读取n个字符时及没有遇到 \n 也没有EOF,此时只能读n-1个字符,因为最后一个字符被 \0替代

​ 遇到EOF时结束,不读EOF

rewind()函数,将FILE中的flag指向文件开头

一些套路操作

#include <stdio.h>
//定义一个宏函数,‘\’这个是连接符,因为在宏定义是不可以换行的
#define F_PRINT_ERR(e)\
	if(e == NULL);\
	{\
		printf("openerr");\
		exit(-1);\
	}\
int main()
{
	FILE *fp = fopen("xx.txt","w+");
	F_PRINT_ERR(fp);
}

C语言已经从接口层面区分了,文本读写的方式和二进制读写的方式。前面都是文本的读写方式

所有文件接口函数,要么以’\0’,表示输入结束,要么以 ‘\n’ EOF(0xFF) 表示读取结束。 ‘\0’ '\n’等都是文本文件的主要标志,而二进制文件,则往往以块的形式写入或读出

对二进制文件读写

fread() / fwrite()

函数声明:int fwrite(void *buffer,int num_bytes,int count,FILE *fp)

​ int fread(void *buffer,int num_bytes,int count,FILE *fp)

功能:将buffer指向的数据写入 fp 所指向的文案中,或把fp所指向的文件中的数据读到buffer中

参数:

char * buffer :指向要输入输出数据的存储区的首地址的指针

int num_bytes:每个要读写的字段的字节数

int count :要读写的字段的个数

FILE *fp:要读写的文件指针

返回值: int 成功返回读/写的字段数;出错或文件结尾返回 0

这种读写是在字节层面上的,就是内存中是什么就读写什么,当人这就方便了字符的读写,因为字符在文本格式和二进制模式下都是ASCII码的形式,但是数字就不同了,文本是将数字分成多个部分,每部分用ASCII表示,而二进制是用补码的形式来表示

#include<stdio.h>
int main(int argc,char *argv[])
{
    FILE *fp = fopen("gg.txt","w+");
    char arr[10] = {'a','v','l','u','b','n','a','t','r','p'};
    fwrite((void*)arr,1,10,fp);
    char buf[10];
    rewind(fp);  //将fp结构体内flag指向文件头部
    fread((void*)buf,1,10,fp);
    for(int i=0;i<10;i++)
    {
        printf("%c\n",buf[i]);
    }
    return 0;
}
返回值陷阱

fread()没有 \n EOF len - 1 作为读写结束的标志,fread依靠读出多少块来标识读结果和文件结束标志,这里指的块是完整的块

int fread(void *buffer,int num_bytes,int count,FILE *fp)这里的 count 指的是可以读到的最大块数,而numo_bytes指的是每次读的字节数,而返回的是读到的完整块数

#include<stdio.h>
int main(int argc,char *argv[])
{
    char buf[1024];
    FILE *pf = fopen("mm.tst","w+");
    fputs("i love you",pf);
    rewind(pf);
    int n = fread((void*)buf,3,10,pf);
    printf("%d\n",n);
    for( int i=0;i<10;i++)
    {
        printf("%c",buf[i]);
    }

    return 0;
}
终端输出
3
i love you

这种写法是不利于循环的,优化方法就是令每次读写的字节数与要读取的sizeof()所得的值相同,这样就可以使当返回值为 0 时程序就刚好读完。从而使效率最大化

vscode配置:

基于 VS Code + MinGW-w64 的C语言/C++简单环境配置,专致小白 - 知乎 (zhihu.com)

%c\n",buf[i]);
}
return 0;
}


#### 返回值陷阱

fread()没有 \n EOF len - 1 作为读写结束的标志,fread依靠读出多少块来标识读结果和文件结束标志,这里指的块是完整的块

int fread(void *buffer,int num_bytes,int count,FILE *fp)这里的 count 指的是可以读到的最大块数,而numo_bytes指的是每次读的字节数,而返回的是读到的完整块数

#include<stdio.h>
int main(int argc,char *argv[])
{
char buf[1024];
FILE pf = fopen(“mm.tst”,“w+”);
fputs(“i love you”,pf);
rewind(pf);
int n = fread((void
)buf,3,10,pf);
printf(“%d\n”,n);
for( int i=0;i<10;i++)
{
printf(“%c”,buf[i]);
}

return 0;

}
终端输出
3
i love you


这种写法是不利于循环的,优化方法就是令每次读写的字节数与要读取的sizeof()所得的值相同,这样就可以使当返回值为 0 时程序就刚好读完。从而使效率最大化

























vscode配置:

[基于 VS Code + MinGW-w64 的C语言/C++简单环境配置,专致小白 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/77074009)







 



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值