C语言b站bit知识点

目录

一、qsort函数

1.库函数qsort函数

2.自己实现的qsort函数

二、大小端字节序存储模式

三、截断与整型提升

四、字符串处理函数

1.strlen函数

2.strcpy函数

3.strcat函数

4.strcmp函数

5.strncopy函数

 6.strncat函数

7.strncmp函数

8.strstr函数

五、内存处理函数

1.memcpy函数

2.memmove函数

3.memcmp函数

4.memset函数

六、自定义数据类型

1.结构体

2.共用体(联合体)

3.枚举常量

七、函数的值传递和址传递

1.值传递:

2.址传递

*八、返回栈空间地址导致代码错误

九、缓冲文件系统的输入/输出函数

1.gets/puts函数

2.fgets/fputs函数

3.fgetc/fputc函数

4.scanf/printf函数

5.fscanf/fprintf函数

6.sscanf/sprintf函数

7.fopen/fread/fwite/fclose函数

8.fseek/ftell/rewind函数

9.strerror/perror函数

10.ferror/feof函数

十、C语言预处理

1.源文件 --> 可执行文件

 2.系统的预定义符号

3.#define定义的符号和宏

4.#define里边的#和##

十一、数学函数

1.fmax/fmin


一、qsort函数

1.库函数qsort函数

((void*)空指针类型:

可以接受所有类型的数据的地址,不能直接接引用和加减操作访问地址对应的值,在解引用前应进行强制类型转换(例如e1为void*类型指针,原*e1对应的数据类型为整型,正确的解引用操作为*(*int)e1)

①函数功能:实现对任意类型数据的排序

②引用头文件#include<stdlib.h>

③函数原型:void qsort(void *start,int length,int width,int(*cmp)(const void*e1,const void* e2));

start:待排序数组的首元素的地址

length:数组的元素个数

width:数组的元素大小

int(*cmp)(const void* e1,const void* e2):比较函数指针

④比较函数写法:

因为涉及到传参过程,需要保证函数的参数以及返回值类型保持一致:

int _cmp(const void* e1,const void* e2);

e1>e2返回值大于0(升序排列)

e1=e2返回值等于0

e1<e2返回大于0(降序排列)

当函数返回值大于0时便会进行两个元素之间的交换

⑤对不同类型数据排序的写法:

a.整型:

qsort函数:
qsort(arr,sz,sizeof(arr[0]),cmp_int);
比较函数写法:
int cmp_int(const void* e1,const void* e2)
{
    return *(int*)e1-*(int*)e2;//升序排列
或者:return *(int*)e2-*(int*)e1;//降序排列
}

b.浮点型:

qsort函数:
qsort(f,sz,sizeof(f[0]),cmp_float);
比较函数:
com_float(const void* e1,const void* e2)
{
    if(*(float*)e1==*(float*)e2)
        return 0;
    else if(*(float*)e1>*(float*)e2)//升序排列  降序排列:(*(float*)e1<*(float*)e2)
        return 1;
    else
        return -1;
}

c.结构体:

struct Stu
{
	int age;
	char name[10];
};
int cmp_struct_by_age(const void* e1, const void* e2)//比较函数定义方法
{
	(升序排列)return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;//注意访问成员变量的方法
    (降序排列)return ((struct Stu*)e2)->age - ((struct Stu*)e1)->age;
}
int main()
{
	struct Stu stu[3] = { {18,"孙悟空"},{19,"猪八戒"},{20,"唐僧"} };
	int sz = sizeof(stu) / sizeof(stu[0]);
	qsort(stu, sz, sizeof(stu[0]), cmp_struct_by_age);
	for (int i = 0; i < sz; i++)
	{
		printf("%d  %s\n", (stu + i)->age, stu[i].name);
	}

2.自己实现的qsort函数

#include<stdio.h>
void swap(char* e1, char* e2, int width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *e1;
		*e1 = *e2;
		*e2 = tmp;
		e1++;
		e2++;
	}
}
void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))//注意函数指针参数
{
	for (int i = 0; i < sz-1; i++)
	{
		for (int j = 0; j < sz-1-i; j++)
		{
			if ((*cmp)((char*)base+j*width,(char*)base+(j+1)*width)>0)//强转为char*类型,加上宽度来达到控制任意数据类型地址的目的
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
int cmp_int(char* e1, char* e2)//整型交换函数
{
	if (*(int*)e1 < *(int*)e2)//降序排列
	{
		return 1;
	}
	else if (*(int*)e1 == *(int*)e2)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}
int cmp_float(void* e1, void* e2)//浮点型交换函数
{
	if (*(float*)e1 > *(float*)e2)//升序排列
	{
		return 1;
	}
	else if (*(float*)e1 == *(float*)e2)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}
void Print_int(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
void Print_float(float f[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%.1f ", f[i]);
	}
}
int main()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,10,0 };
	int sz_arr = sizeof(arr) / sizeof(arr[0]);
	float f[] = { 2.0,3.6,5.8,9.4,5.5,2.8,7.1 };
	int sz_f = sizeof(f) / sizeof(f[0]);
	bubble_sort(arr, sz_arr, sizeof(arr[0]), cmp_int);
	Print_int(arr, sz_arr);
	bubble_sort(f, sz_f, sizeof(f[0]), cmp_float);
	Print_float(f, sz_f);
	return 0;
}

二、大小端字节序存储模式

1.图示:

大端存储:低字节存放在高地址处,高字节存放在低地址处。

小端存储:低字节存放在低地址处,高字节存放在高地址处。

2.判断电脑大小端

通过共用体判断
#include<stdio.h>
union Judge
{
	short a;//2字节
	char arr[2];//1字节*2
};
int main()
{
	union Judge judge;
	judge.a = 0x1234;
	printf("%x\n", judge.arr[0]);//虽然没有对arr赋值,但作为共用体,它的内存空间已经被占用了
	printf("%x\n", judge.arr[1]);
	return 0;
}
打印结果:34,12.
地字节存放在了低地址,说明当前机器存储类型为小端存储

三、截断与整型提升

1.char类型数能够表示的范围:

char类型数据占一个字节,8个比特位,最高位为符号位,能表示的数据范围为-128-127(2^7-1),规定10000000为-128.
unsigned char类型数据占一个字节,8个比特位,能表示的数据范围为0-255(2^8-1).

图示:

2.整型提升:

对于有符号数,整型提升时高位补符号位。

对于无符号数,整型提升时高位补零。

例如:

unsigned char a=200;
unsigned char b=100;
unsigned char c=a+b;
printf("%d,%d",a+b,c);
---------------------
运行结果应为300,44.
char类型a的二进制表示形式为11001000,char类型b的二进制表示形式为01100100;
二者在进行加减操作之前先进行整型提升,由于无符号数整型提升时最高位补零,因此整型提升后的结果为:
a:00000000000000000000000011001000;
b:00000000000000000000000001100100;
a+b:00000000000000000000000100101100;
无符号数原反补码相同,又c中最多能存放8个比特位,因此发生截断,截断后c内存存放00101100,对应十进制数字44.

四、字符串处理函数

一,长度不受限制的字符串处理函数

1.strlen函数

1.函数原型:size_t  strlen(const char*str);   size_t为typedef重命名的unsigned int

2.返回值:无符号整数

3.函数功能:求字符串长度

4.实现原理:遇‘\0’计数器停止计数,返回‘\0’前的字符数

例如:

int n=strlen("abcdef");
则a=6;

2.strcpy函数

1.函数原型:char* strcpy(char* dst,const char* src);

2.函数功能:将src字符串内容复制到dst字符串中,使得dst和src的内容一致.

3.实现原理:将src字符串中的‘\0’也复制了过去

4.返回值:返回dst字符串的首地址

例如:

char dst[]="abcdef";
char src="efg";
strcpy(dst,src);
det字符串的内容变为:"efg"

3.strcat函数

1.将字符串src连接到字符串dst后,包含‘\0’.

2.函数原型:char* strcat(char *dst,const char* src);

3.实现原理:从dst字符串末尾的‘\0’(包含‘\0’)开始,将src字符串连接,包含src末尾的'\0'.

4.函数返回值:返回连接后的字符串的首地址.

例如:

char str[30] = "abcde";
char src[] = "fgghj";
char *p = strcat(str, src);
printf("%s", str);

4.strcmp函数

1.函数原型:int strcmp(const char* str1,const char* str2);

2.函数功能:比较两个字符串的大小。

3.实现原理:比较两个字符串的同一个位置字符,相等继续比较下一个位置的字符,没有遇到'\0'前,若字符串1同一位置字符的ascii码值大于第二个字符串相同位置的字符的ascii码值,则字符串1大于字符串2,返回一个大于0的数字;反之,返回一个小于0的数字。若2者都比较到‘\0’的位置,则字符串1等于字符串2,返回数字0.

4.例子:

char* str1 = "abcdef";
char* str2 = "abcdee";
int ret = strcmp(str1, str2);
ret应为大于零的数

二,长度受限制的字符串处理函数

5.strncopy函数

1.函数原型:int strncpy(char *dst,const char* src,size_t num);

size_t:==unsigned int;  num待拷贝的字节数

2.函数功能:将src字符串里的num个字符拷贝到字符串dst中。

3.注意事项:拷贝num个字符后,不再dst末尾追加‘\0’;当拷贝数大于src实际字符数时,在dst的末尾追加‘\0’直至拷贝num个字符。

例子:

char str1[] = "abcdef";
char str2[] = "def";
strncpy(str1, str2, 3);
输出str1:defdef

*如果待拷贝字符数大于src实际的字符数,则在dst末尾追加'\0'直至拷贝num个字符:
char str1[10] = "abcdefgh";
char str2[] = "def";
strncpy(str1, str2, 6);
则拷贝后dst的内容为def;
如下图:

 6.strncat函数

1.函数原型:char* strncat(char* dst, ocnst char*src, size_t num);

2.函数功能:将src中的n个字符追加到字符串dst的末尾,并在追加结束后在dst的末尾放上'\0'际的;当录入的字符个数大于src字符串的长度时,正常进行追加后再dst末尾追加‘\0’后,多余的要追加的字符个数不做处理。

3.例子:

char dst[30] = "abcdefg";
char src[] = "hijklmn";
strncat(dst, src, 3);

输出结果dst:abcdefghij

7.strncmp函数

1.函数原型:int strncmp(const char* str1,const char* str2,size_t count);

2.函数功能:将str1与str2中的n个字符进行比较,原理与strcmp相似。

3.例子:

char str1[] = "abcd";
char str2[] = "abcdefg";
int ret = strncmp(str1, str2, 5);

结果:ret<0

8.strstr函数

1.函数原型char* strstr(const char*str1,const char*str2);

2.函数功能:找出str2字符串在str1字符串中首次出现的位置,(不包括str2的串结束符)。

如果找到了,返回子串第一次出现的位置的指针,如果没有找到,返回NULL。

3.例如:

char str1[]="abcdef";
char str2[]="cdef";
char* ret=strstr(str1,str2);
if(ret==NULL)
{
    return -1;//没有找到
}
else
{
    return 1;
}

五、内存处理函数

*头文件:string.h

1.memcpy函数

1.函数原型:voi* memcpy(vo

id* dst,const void*src,size_t num);

num:拷贝的字节数,单位字节。

2.函数功能:处理没有交叉的数据的拷贝实现任意类型数据间的拷贝,也可以处理交叉数据。

3.实现原理:

while (num)
{
    *(char*)dst = *(char*)src;
    ((char*)dst)++, ((char*)src)++;
    num--;
}

4.例子:

int dst[10] = { 0 };
int src[] = { 1,2,3,4,5 };
memcpy(dst, src, sizeof(src));
运行结束后dst:1,2,3,4,5

如果:
int arr[]={1,2,3,4,5,6,7,8,9};
memcpy(arr+2,arr,4*5);
memcpy无法处理此类交叉数据的拷贝,需要用到内存处理函数memmove()

2.memmove函数

1.函数原型:void* memmove(void* dst,const char* src,size_t num);

2.函数功能,实现原理:与函数memcpy类似,可用于处理交叉数据的拷贝

3.例子:

处理重叠:
int arr[] = { 1,2,3,4,5,6,7,8,9 };
memmovev(arr + 2, arr, 4 * 5);
结果arr:1,2,1,2,3,4,5,8,9

处理不重叠:
和memcpy相同

4.模拟实现:

//自己的写法:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
void* My_memmove(void* dst, const void* src, size_t num)
{
	assert(dst && src);//1,2,3,4,5,6,7,8,9,10
	void* p = dst;
	const void* pl = src;
	if ((char*)src + num < (char*)dst)//没有交叉
	{
		while (num--)
		{
			*(char*)dst = *(char*)src;
			++(char*)dst, ++(char*)src;
		}
		return p;
	}
	else//有交叉
	{
		/*建立一个数组用来存放与src相同的内容*/
		void* pr = malloc(num);
		void* ptr = pr;//指向动态内存pr的指针
		/*int num_src = ;
		while (num_src--)
		{
			*(char*)ptr = *(char*)src;
			++(char*)ptr;
			++(char*)src;
		}*/
		//如何的到一摸一样的数组
		int num_cpy = (int)num;
		while (num_cpy--)
		{
			*(char*)ptr = *(char*)src;
			++(char*)ptr, ++(char*)src;
		}
		/*进行拷贝*/
		ptr = pr;
		while (num--)
		{
			*(char*)dst = *(char*)ptr;
			++(char*)dst, ++(char*)ptr;;
		}
		return p;
	}
}
int main()
{
	int dst[10] = { 5,5,5,5,5 };
	int src[] = { 6,6,6, };
	My_memmove(dst, src, sizeof(src));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(dst + i));
	}
	return 0;
}

3.memcmp函数

1.函数原型:int memcmp(const void* arr1,const void* arr2,size_t num);

2.函数功能:比较两个数据类型的num个字节是否相同,相同返回0,arr1>arr2返回大于0的数字,arr1<arr2返回小于0的数字。

例如:

int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 1,2,3,3,3 };
int ret = memcmp(arr1, arr2, 4 * 4);

结果:ret=1>0

4.memset函数

1.函数原型:void* memset(void* arr,int set_char,size_t num);

其中:set_char:设置的内容;num:字节数,单位字节。

2.函数功能:为内存赋值。

3.例子:

char str[10] = { 0 };
memset(str, '#', sizeof(str));

结果str:##########

六、自定义数据类型

*结构体,枚举,联合体

1.结构体

1.定义与初始化:c语言大学使用教程。

2.使用的注意事项:

①、
typedef struct S
{
  int a;
  char name[20];
}S;
*使用了typedef关键字将struct S重命名为S,之后创建变量:
struct S s1;等价于S s1;

②、同一结构体的成员变量不能包含自身,例如:
strcut node
{
  int a;
  struct node s1;  //无法为该结构体类型申请确定大小的空间
};是错误的
要写成:
strcut node
{
  int a;
  struct node* next;  
};

3.内存对齐

①、结构体对齐规则:

*第一个成员在与结构体变量偏移量为0的地址处;

*其他成员要对齐到某个数字(对齐数)的整数倍处;

*对齐数:编译器默认的对齐数与该成员变量所占内存的较小值,VS默认值为8

*结构体总大小为成员变量里边的最大对齐数的整数倍;如果嵌套了结构体,嵌套的结构体对

齐到自己最大的对齐数的整数倍处,结构体的整体大小就是所有对齐数的最大值(包含嵌套的结构体)的整数倍;

②、结构体默认对齐数可以用预处理指令进行修改:

#pragma	pack(2)//设置默认对齐数为2
struct S
{
	char a;
	int b;
	int c;
};
#pragma	pack()//取消设置的对齐数,还原默认数值




*未设置默认对齐数时的结构体大小为12;
*设置对齐数后的结构体大小为10;

4.位段

①、定义方式类似于结构体:

struct S
{
	int _a : 5;
	int _b : 10;
	int _c : 14;
	int _d : 22;
};

上述位段所占的存储空间为8个字节,冒号后的数字为数据所占比特位数;
在为该位段分配储存空间时,先查阅到int类型,开辟4个字节32个比特位的空间,可以存下a,b,c共29个比特位的数据,再存d时剩余的3个比特位不足以存放,再开辟新的存储空间即4个字节32个比特位存放d,因此上述位段共占8个字节的空间

2.共用体(联合体)

1.声明样式:

union S
{
  int a;
  char str[2];
};

2.共用体内成员共同占用同一块存储空间,所占存储空间的大小为成员中所占空间大小的最大值,如上述共用体所占内存空间的大小为4字节。

3.枚举常量

1.声明样式:

enum  Myenum
{  exp1,
   exp2,
   exp3……
};

2.枚举常量可以指定值也可以不指定。不指定值时枚举常量的值时从第一个成员开始依次为0,1,2……,指定成员变量的值时,以下的成员依次递增1。

①enum MyEnum
{
	ONE,//值为0
	TWO=5,
	THREE//值为6
};


②enum MyEnum
{
	ONE,//值为0
	TWO,//值为1
	THREE.。值为2
};



③enum MyEnum
{
	ONE=5,
	TWO,//值为6
	THREE//值为7
};

七、函数的值传递和址传递

1.值传递:

传递常量或者变量的数值

:例如:

值传递①:
void Modify(int a)
{
	a = 10;
}
int main()
{
	int a = 5;
	Modify(a);
	printf("%d\n", a);
	return 0;
}

值传递②:
void Modify(int* p)
{
	*p = 10;
}
int main()
{
	int a = 5;
	int* p = &a;
	Modify(p);//传参是一个指针变量,所以用指针变量接收
	printf("%d\n", a);
	return 0;
}//传递了指针变量p的值,在函数内部开辟了一个新的指针变量p`存放了与指针p相同的指向整型a的地址,并非址传递

2.址传递

传递常量或者变量的地址

例如:

址传递①:
void Modify(int* p)
{
	*p = 10;
}
int main()
{
	int a = 5;
	Modify(&a);//实参传递了整型常量a的地址,所以形参用指针变量接收
	printf("%d\n", a);
	return 0;
}

址传递②:
void Modify(int** p)//用二级指针接收(存放一级指针变量的地址)
{
	*(*p) = 10;
}
int main()
{
	int a = 5;
	int* p = &a;
	Modify(&p);//传递了指针变量的地址
	printf("%d\n", a);
	return 0;
}

3.面试题:

判断运行结果,找出代码中的错误:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Getmemory(char* p)//形参用指针变量接收
{
	p = (char*)malloc(100);//p在函数内部申请内存空间后未释放也无法在函数外部释放,造成了内存泄漏
	if (p == NULL)
	{
		exit(0);
	}
}
int main()
{
	char* str = NULL;
	Getmemory(str);//错误①,使用了值传递,传递了指针变量的值,在函数中不能为str分配内存空间
	strcpy(str, "Hellow,world");
	printf(str);
	free(str);
	str = NULL;
	return 0;
}

 *正确解法:

正确解法①(址传递):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Getmemory(char** pr)//形参设计为二级指针(接收一级指针变量的地址)
{
	*pr = (char*)malloc(100);//*pr找到了原指针变量p里存放的NULL,malloc函数申请100个字节的返回首地址存放到原指针变量p中去,并且在此处申请的空间位于堆区上,出函数体并不会被销毁
	if (*pr == NULL)
	{
		exit(0);
	}
}
int main()
{
	char* str = NULL;
	Getmemory(&str);//址传递,传递指针变量的地址
	strcpy(str, "Hellow,world");
	printf(str);
	free(str);
	str = NULL;
	return 0;
}

正确解法(函数返回值)②:
char* Getmemory()
{
	char* p = (char*)malloc(100);//申请的内存空间位于堆上,出函数体后p被销毁,但是申请的内存空间不会被销毁。
	if (p == NULL)
	{
		return NULL;//不成功返回空指针
	}
	return p;//申请成功返回申请成功后的地址
}
int main()
{
	char* str = NULL;
	str = Getmemory();
	if (str == NULL)
	{
		exit(0);
	}
	strcpy(str, "Hellow,world");
	printf(str);
	free(str);
	str = NULL;
	return 0;
}

*八、返回栈空间地址导致代码错误

栈区:存放局部变量,函数形参,返回数据等

1.面试题:

#include<stdio.h>
char* Getmemory()
{
	char p[] = "Hellow,world";//栈区开辟的空间,在出函数体后被销毁,str虽然得到了返回的地址,但是该地址对应的内存上的内容已经被销毁
	return p;
}
int main()
{
	char* str = NULL;
	str = Getmemory();
	printf(str);
	return 0;
}

正确写法:

①在p[]前面加上static使得开辟的存库空间位于静态存储区,修改p数组的生命周期
在函数Getmemory内部:
static p[] = "Hellow,world";

九、缓冲文件系统的输入/输出函数

1.gets/puts函数

1. gets:
   函数原型:char* gets(str)
   函数作用:从标准输入设备(键盘)读入字符串,放到str指向的字符数组中一直读到换行符或者EOF为止,换行符不作为读入内容,转换为'\0'作为字符串结束标志。
   函数返回值:读入成功返回str指针,失败返回NULL。
2. puts:
   函数原型:int puts(str)
   函数作用:将str指向的字符串输出到表追输出设备(屏幕)上,把‘\0’转换为回车换行,即puts函数在使用后自动换行。
   返回值:返回换行符,失败返回EOF。

2.fgets/fputs函数

1. fgets: 
   函数原型:char* fsets(char* buffer,int count,FILE* stream)
   函数功能:从stream指向的文件读取一个长度为(count-1)的字符串,存入起始地址为buffer的空间,与gets不同的是,fgets将换行符也字符串的一部分读入。读到换行符终止
   返回值:函数地址buffer;若与问价你结束或者出错,返回NULL。
例如:FILE* pf = fopen("baixiaoshuai.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fgets(p, 5, pf);//从文件读取长度为4的字符串放到字符数组p中。
2. fputs: 
   函数原型:int fputs(const char* buffer,FILE stream)
   函数功能:将buffer指向的字符串输出到stream所对应的文件,与puts不同的是fputs不会在末尾加上换行符。
   返回值:成功返回0;出错返回非0.
例如:FILE* pf = fopen("baixiaoshuai.txt", "w");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fputs("baixiaoshuai", pf);//将baixiaoshuai输出到文件中。

3.fgetc/fputc函数

fgetc:
    函数原型:int fgetc(FILE* pf)
    函数功能:从pf指定的文件中取得下一个字符
    返回值:返回得到的字符,若读入出错,返回EOF。
    例如:FILE* pf = fopen("baixiaoshuai.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	printf("%c", fgetc(pf));
	fclose(pf);
	pf = NULL;
fputc:
    函数原型:int fputc(int ch,FLIE* pf)
    函数功能:将字符ch输出到pf指定的文件中,(尽管ch为int型,但只写入低字节)。
    返回值:成功返回该字符,否则返回EOF。
    例如:FILE* pf = fopen("baixiaoshuai.txt", "w");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));//头文件:string.h/errno.h
		return 0;
	}
	fputc('j', pf);
	fclose(pf);
	pf = NULL;

4.scanf/printf函数

scanf:针对标准输入流i的格式化输入语句。
    函数原型:int scanf(const char*format,args,......)
    函数功能:从标准输入设备(键盘)按format指向的字符串规定的格式,输入数据给args所指向的单元。以%s格式输入字符串时,遇到空白字符(包含空格、换行符、制表符)时,系统会认为读入结束,(但在开始读之前遇到的空白字符会被系统自动跳过)。
    返回值:读入并赋给args数据的个数。遇文件出错返回EOF。出错返回0.
printf:针对标准输出流的格式化输出语句。
    函数原型:int printf(const char*format,args.....)
    函数功能:将输出表列args的值输出到标准输出设备(屏幕).
    返回值:输出字符的个数;若出错,返回负数

5.fscanf/fprintf函数

fscanf:针对所有输入流的格式化输入语句。
    函数原型:int fscanf(FILE* const pf,const char*format,args,......)
    函数功能:从pf指定的文件中按format给定的格式输入数据到args所指向的内存单元
    返回值:已输入数据的个数
    例如:#include<stdio.h>
#include<string.h>
#include<errno.h>
struct Peo
{
	int age;
	char sex[6];
	char name[20];
}peo;
int main()
{
	FILE* pf = fopen("baixiaoshuai.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fscanf(pf, "%d %s %s", &(peo.age), peo.sex, peo.name);//从pf指向的文件流中读取数据存到相应的存储单元
	fclose(pf);
	pf = NULL;
	printf("%s\n", peo.name);
	return 0;
}


fprintf:针对所有输出流的格式化输出语句。
    函数原型:int fprintf(FILE* const pf,const char*format,args,....)
    函数功能:把args的值以format的格式输出到pf指定的文件中。
    返回值:实际输出的字符数
    例如:struct Peo
{
	int age;
	char sex[6];
	char name[20];
};
int main()
{
	struct Peo peo = { 20,"nan","baixiaoshuai" };
	FILE* pf = fopen("baixiaoshuai.txt", "w");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fprintf(pf, "%d %s %s", peo.age, peo.sex, peo.name);//将内存单元中的数据格式化输出到pf指向的文件中。
	fclose(pf);
	pf = NULL;
	return 0;
}

6.sscanf/sprintf函数

sscanf:
    函数原型:int sscanf(connst char* buffer,const char* format,args,....)
    函数功能:从字符串读取格式化数据。
    返回值:返回成功读入的格式化数据个数,出现错误返回EOF,未分配返回0.
    例如:#include<stdio.h>
    typedef struct Stu
    {
    	int age;
    	char sex[6];
    	char name[20];
    }Peo;
    int main()
    {
    	Peo peo = { 0 };
    	char str[20] = "18 nan baixiaoshuai";
    	sscanf(str, "%d %s %s", &(peo.age), peo.sex, peo.name);//从字符串格式化输入数据
    	return 0;
    }

sprintf:
    函数原型:int sprintf()
    函数功能:将格式化数据写入字符串。(将格式化数据转换为字符串)
    返回值:返回存储在缓冲区的字节数。不计算终止的空字符
    例如:#include<stdio.h>
    typedef struct Stu
    {
	    int age;
	    char sex[6];
	    char name[20];
    }Peo;
    int main()
    {
    	Peo peo = { 18,"nan","baixiaoshuai" };
    	char str[60] = { 0 };
    	sprintf(str, "%d %s %s", peo.age, peo.sex, peo.name);//将格式化数据peo转换为字符串存储到字符数组str中。
    	printf(str);
    	return 0;
    }

7.fopen/fread/fwite/fclose函数

1.fopen:
    函数原型:FILE* fopen(const char* File_name,const char* Mode)
    函数功能:以Mode指定的模式打开一个名为File_name的文件。
    返回值:成功,返回一个文件指针;失败返回NULL,错误代码在errno中(strerror(errno))。
    例如:FILE* pf = fopen("baixiaoshuai.txt", "r"/*只读的方式*/);//w:写的方式打开,文件存在则覆盖,文件不存在则新建
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//成功打开
	//....
    fclose(pf);
    pf=NULL;

2.fread:
    函数原型:size_t fread(void* buffer,size_t Element_size,size_t Element_count,FILE* pf)
    函数功能:从pf指向的文件中读取size*count个字节存到buffer指向的内存区。
    返回值:返回Element_count(数据项个数)。若遇文件结束或出错,返回0.
    例如:#include<stdio.h>
    #include<string.h>
    #include<errno.h>
    int main()
    {
    	char str[30] = { 0 };
	    FILE* pf = fopen("baixiaoshuai.txt", "r"/*只读的方式*/);
    	if (pf == NULL)
    	{
	    	printf("%s\n", strerror(errno));
	       	return 0;
	    }
	    //成功打开
	    fread(str, sizeof(char), 5, pf);//不会在字符串末尾添加'\0'
	    printf(str);
        fclose(pf);
        pf=NULL;
	    return 0;
}
3.fwrite:
    函数原型:size_t fwrite(const void* buffer,size_t Element_size,size_t Element_count,File* pf)
    函数功能:从str所指向空间的size*count个字节输出到fp所指向的文件中。
    返回值:写到fp文件中的数据项的个数(count)
    例如:
int main()
{
	char str[] ="baixiaoshuai";
	FILE* pf = fopen("baixiaoshuai.txt", "w"/*只读的方式*/);
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//成功打开
	fwrite(str, sizeof(char), strlen(str), pf);//从str指向的内存块中读取1*16个字节输出到pf对应的文件baixiaoshuai.txt中。
	fclose(pf);
	pf = NULL;
	return 0;
}
4.fclose:
    函数原型:int fclose(FILE* pf)
    函数功能:关闭fp指向的文件。
    返回值:成功返回0,否则返回非0。
    例如:FILE* pf = fopen("baixiaoshuai.txt", "w"/*只读的方式*/);
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//成功打开
	fclose(pf);//关闭文件

8.fseek/ftell/rewind函数

1.fseek:
    函数原型:intt fseek(FILE* pf,long offset,int origin)
    函数功能:移动文件指针到特定的位置。
    返回值:成功返回,否则返回非0.在无法搜索的设备上,返回值未定义
    ogigin取值及其含义:SEEK_CUR---从文件指针当前位置开始偏移
                      SEEK_END---从文件指针末尾开始偏移
                      SEEK_SET---从文件指针起始位置偏移
    例如:
    #include<stdio.h>
    int main()
    {
	    FILE* pf = fopen("text.txt", "r");//文件中存的是agcdef
	    if (pf == NULL)
	    {
		    perror("text.txt error");//输出错误信息样式:text.txt error:No Such File Or Directory.
		    return 0;
	    }
	    //成功打开
	    fseek(pf, -2, SEEK_END);
	    printf("%c\n", fgetc(pf));//e
	    fclose(pf);
	    pf = NULL;
    }

2.ftell:
    函数原型:long ftell(FILE* pf)
    函数功能:获得当前文件指针的位置
    返回值:返回文件指针相对于起始位置的偏移量
    例如:
    fseek(pf, 0, SEEK_END);//将文件指针置于文件末尾
	int ch = ftell(pf);//返回文件指针相对于起始位置的偏移量
	printf("%d\n", ch);
    可以很快知道文件的长度

3.rewind
    函数原型:void rewind(FILE* pf)
    函数功能:将文件指针重新定位与文件的开头
    例如:
    fseek(pf, -2, SEEK_END);//将位置指针置于末尾向前偏移2字节的位置
	int ch = ftell(pf);//返回当前文件指针的位置
	printf("%d\n", ch);
	rewind(pf);//将文件指针置于文件的起始位置
	printf("%d\n", ftell(pf));
	fclose(pf);//关闭文件
	pf = NULL;//将pf指针置为空

9.strerror/perror函数

1.strerror:
    函数原型:char* strerror(int errnum)
    函数功能:提供系统错误消息或打印用户提供的错误消息
    返回值:返回指向错误消息字符串的指针
    例如:
    FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)//以只读的方式打开文件
	{
		printf("%s\n", strerror(errno));//失败的话则输出错误信息
		return 0;
	}
    fclose(pf);
    pf = NULL;

2.perror:
    函数原型:void perror(const char* str)
    函数功能:打印错误信息
    例如:
    FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)//若打开文件失败
	{
		perror("error");//输出错误信息error:No such file or directory
		return 0;
	}
    fclose(pf);
    pf = NULL;

10.ferror/feof函数

1.ferror:
    函数原型:int error(FILE* pf)
    函数功能:检测文件流上的错误
    返回值:如果流上没有没有发生错误,返回0;否则返回非0.

2.feof
    函数原型:int feof(FILE* pf)
    函数功能:测试流上文件结束
    返回值:读到文件末尾返回非0;否则返回0


*ferror和feof常用来判断文件是否正常结束:
int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (!pf)
	{
		perror("error");//打开文件失败返回输出错误信息
		return 0;
	}
	int c = 0;
	while ((c = fgetc(pf)) != EOF);
	if (ferror(pf))//文件异常结束,返回非0
		printf("文件读取错误结束\n");
	else if (feof(pf))//文件正常结束返回非0
		printf("文件正常结束\n");
	fclose(pf);
	pf = NULL;
}

十、C语言预处理

1.源文件 --> 可执行文件

①源文件(.c)经过编译器编译、链接器链接生成目标文件(.obj)。

②编译的过程又可以分为:预编译/预处理、编译、汇编。

   预处理阶段进行的操作有引用包含文件(#include),替换#define定义的符号,注释删除(使用空格代替注释);编译阶段进行的操作有将C语言代码转换为汇编代码,发生语法分析,词法分析,语义分析,符号汇总;汇编阶段进行的操作有把汇编代码转化为二进制值指令,形成符号表;链接阶段进行的操作有合并段表(elf格式),符号表的合并和重定位。

③如图:

 2.系统的预定义符号

①、__FILE__       //打印文件名称
②、__LINE__       //打印当前行数
③、__DATE__       //打印文件的编译日期
④、__TIME__       //打印文件编译时间
⑤、__STDC__       //如果编译器支持ANSI C,其值为1,否则未定义

例如:
	printf("%s\n", __FILE__);//输出文件路径及名称
	printf("%d\n", __LINE__);//输出当前的行数
	printf("%s\n", __DATE__);//输出文件被编译的日期
	printf("%s\n", __TIME__);//输出文件被编译的时间
    printf("%d\n", __STDC__);//检查编译器是否遵循ANSC标准,如果遵循返回1,否则未定义

3.#define定义的符号和宏

1.#define定义的符号
#define  MAX    100
*在预处理阶段完成符号的替换,#define在定义符号时,最好不要在值的末尾加上分号



2.#define定义的宏
#define  NAME(LIST)    STUFF
例如:
#define  SQUARE(X)    ((X)*(X))
在预编译阶段,会用((X)*(X))来替换,加上括号的原因是考虑到运算符的优先级可能会导致运算出错,比如:
#define  SQUARE(X)   X*X
int a = SQUARE(5+1);
则a的值为11,这是因为在与编译阶段会用5+1替换X出现5+1*5+1,导致结果出错,因此选择对宏的每一个参数都加上括号。
再比如:
#define  DOUBLE(X)   (X)+(X)
int a = 10 * DOUBLE(5);
在运算时会出现10*5+5=55而导致结果出错,因此对于#define定义的宏最好在值的外侧在加上一层括号确保运算顺序的正确。即:
#define  SQUARE(X)   ((X)+(X))最好

4.#define里边的#和##

1.# 
    功能:将宏的参数插入到字符串中
    例如:
    #include<stdio.h>
    #define	PRINT(X)	printf("the value of "#X " is %d\n",X)//在预处理阶段将#X替换为字符串"X"
    int main()
    {
    	int a = 5;
    	int b = 6;
    	PRINT(a);//打印结果为the value of a is 5
    	PRINT(b);//打印结果为the value of b is 6
    	return 0;
    }
2.##
    功能:把位于##两边的符号合成一个符号
    例如:
    #define	BAIXIAOSHUAI(X,Y)	X##Y	
    int main()
    {
	    int ClassWin = 888;
	    printf("%d\n", BAIXIAOSHUAI(Class, Win));//将Class,Win连接成一个符号
    	return 0;
    }

5.#undef移除宏定义  条件编译#ifdef/#endif  避免头文件被多次包含  模拟实现offsetof

1.undef
    功能:移除宏定义
    例如:   #define	MAX		20
            int main()
                {
	            printf("%d\n", MAX);
            #undef	MAX//取消了MAX的定义,下一行无法打印
	            printf("%d\n", MAX);//"MAX"未声明的标识符
	            return 0;
                }

2.条件编译避免头文件被多次包含
①   #ifdef __YUAN_H__
    #define	__YUAN_H__	1
    #include"yuan.h"
    #endif // __YUAN_H__

②   #pragma  once
    #include"yuan.h"

3.模拟实现offsetof(计算偏移量)的宏
#define Offsetof(struct_type,struct_mem_name)		(int)&(((struct_type*)0)->struct_mem_name)

struct S
{
	int a;
	char b;
	int c;
};
int main()
{
	printf("%d\n", Offsetof(struct S, c));
	return 0;
}

十一、数学函数

1.fmax/fmin

函数原型:double fmax(double x,double y);

函数功能:返回x,y的较大值

函数原型:double fmin(double x,double y);

函数功能:返回x,y的较小值

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

whiteInJava

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值