热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

c提高

cxcx

一.内存管理:

内存四区:

技术分享

1.一个C语言变量的作用域可以是代码块 作用域,函数作用域或者文件作用域。
代码块是{}之间的一段代码

2.register寄存器变量
通常变量在内存当中,如果能把变量放到CPU的寄存器里面,代码执行效率会更高
register int I

3.全局函数和静态函数
在C语言中函数默认都是全局的,使用关键字static可以将函数声明为静态,静态函数只能在当前函数内使用

4.代码区 
代码区code,程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,也叫代码段,这块内存是不可以在运行期间修改的。

5.静态区
所有的全局变量以及程序中的静态变量都存储到静态区,比较如下两段代码的区别

    int a =0;

    int main()

    {

       staticint b =0;

       printf("%p, %p\n", &a, &b);

       return0;

    }

    int a =0;

    staticint b =0;

    int main()

    {

       printf("%p, %p\n", &a, &b);

       return0;

    }


6.栈区
栈stack是一种先进后出的内存结构,所有的自动变量,函数的形参都是由编译器自动放出栈中,当一个自动变量超出其作用域时,自动从栈中弹出。

7.堆heap和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。
堆是一个大容器,它的容量要远远大于栈,但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。

8.堆的分配和释放:

malloc:void *malloc(size_t  _size);  malloc函数在堆中分配参数_size指定大小的内存,函数返回void *指针

free:void free(void *p);  free负责在堆中释放malloc分配的内存,参数p为malloc返回的堆中的内存地址

9.malloc、free用法示例:三种结果相同,区别指针用法

第一种:

void getp(int *p){
	printf("%x\n", p);  //31fa48
	printf("%x\n", &p);  //31f974
	printf("%x\n", *p);  //0
	printf("%x\n", *(&p));   //31fa48
	printf("%x\n", **(&p));   //0
	
	*p = malloc(sizeof(int) * 10);
	
}
int main()
{
	int *p = NULL;

	printf("%x\n", &p);   //31fa48
	
	getp(&p);
	p[0] = 1;
	printf("%d\n", p[0]);
	free(p);
	return 0;
}
第二种:
void getp(int **p){
	printf("%x\n", p);  //31fa48
	printf("%x\n", &p);  //31f974
	printf("%x\n", *p);  //0
	printf("%x\n", *(&p));   //31fa48
	printf("%x\n", **(&p));   //0
	
	*p = malloc(sizeof(int) * 10);
	
}
int main()
{
	int *p = NULL;

	printf("%x\n", &p);   //31fa48
	
	getp(&p);
	p[0] = 1;
	printf("%d\n", p[0]);
	free(p);
	return 0;
}
第三种:
void getp(int *p){
	printf("%x\n", p);  //31fa48
	printf("%x\n", &p);  //31f974
	printf("%x\n", *p);  //0
	printf("%x\n", *(&p));   //31fa48
	printf("%x\n", **(&p));   //0
	
	**(&p) = malloc(sizeof(int) * 10);
	
}
int main()
{
	int *p = NULL;

	printf("%x\n", &p);   //31fa48
	
	getp(&p);
	p[0] = 1;
	printf("%d\n", p[0]);
	free(p);
	return 0;
}

第四种:

int *getp()
{
	return malloc(100);
}
int main(){
	int *p = NULL;

	p = getp();

	free(p);
	return 0;
}

10.操作系统在管理内存的时候,最小单位不是字节,而是内存页

11.malloc申请的内存是随机的,不连续的,如果想要申请连续内存空间,可以用calloc,calloc用法跟malloc相同,如果malloc或calloc申请的内存空间不够用,想要申请更多空间,可以用realloc,realloc会在原有内存的基础之上,在堆中间增加连续的内存空间,如果原有内存没有连续空间可扩展,就会新分配一个空间,将原有内存copy到新内存,然后释放原有内存

malloc跟realloc分配的内存都不是连续的;

memset:把malloc或realloc申请的不连续的内存空间变成连续的

写法:

char *p=malloc(10);

char *p1=calloc(10,sizeof(char));

char *p2=realloc(p1, 20);  //在原有内存p1基础上,在堆中增加内存

char *p3=realloc(NULL ,  5);   //等同于malloc(5)

memset(p , 0 ,10);   //将不连续的p内存空间变成连续的,从第0个到第10个

12.static int f=0;用static声明的变量会在整个进程运行期间一直有效,是在静态区,但只能在当前函数内使用

13.如果一个函数要修改另一个函数的静态变量的值,可以通过调用函数的方式将这个静态变量返回然后对其修改

14.堆内存的使用情况:

情况一:int set[1000];   此时set数组在栈中,超过了栈的存储空间,,可以用malloc在堆中创建超大数组。int *set=malloc(sizeof(int)* 100000);

情况二:int i=0;  scanf("%d",&i); int array[i];  这种方式创建动态数组会报错,,应该用malloc创建动态数组。int *array=malloc(sizeof(int)*i);  

二.结构体:

结构体的定义与使用

structman
{
   char name[100];
   int age;
};
struct man m = { "tom", 12 };
struct man m= { .name = "tom", .age = 12 };

1.结构体在内存中是个矩形,而不是一个不规则形状

2.结构体在内存的大小取决于结构体中的内容,同样是int4个字节,char1个字节,但是结构体是4的倍数大小,例:A的大小为8个字节,B的大小为12个字节,c也12个字节

struct A{
	int a;
	char b;
	char c;
}
struct B{
	int a;
	int b;
	char c;
}
struct C{
	char a;
	int b;
	char c;
}

结构体内存大小是以最大长度的内容对齐的,例:D结构体最大长度字节为long long 8个字节,所以都以8对齐,上面的ABC以int(4个字节)对齐,D的大小为24个字节

struct D{
	char a;
	short b;
	char c;
	int e;
	long long d;
}
写成这种方式16个字节:因为short是以2对齐的
struct D {
	char c;
	char a;
	short b;
	int e;
	long long d;
}

3.定义一个结构体的时候可以指定具体元素的位长
	struct test{
		char a : 2;//指定元素为2位长,不是2个字节长
	};

4.结构数组:structman m[10] = { {"tom",12 }, {"marry",10 }, {"jack",9 } };

5.一个结构体的成员还可以是另一个结构体

struct names{
		char first[100];
		char last[100];
	};

	struct man{
		struct names name;
		int age;
	};
	struct man m = { { "wang", "wu" }, 20 };

6.结构体变量之间可以互相赋值(区别数组)

struct str{

	char s[100];
};
int main(){
	
	struct str s1, s2;
	strcpy(s1.s, "hello");//把hello赋给s1.s

	//s2 = s1;	//把s1赋给s2和下面一行代码相同

	//memcpy(&s2, &s1, sizeof(s1));
	printf("%s\n", s2.s);
	return 0;
}
7.指向结构体的指针:定义方式
struct str a;
struct str *p = &a;
(*p).a = 10;
(*p).b= 20;
上面的定义方式等同于下面
p->a = 10;
p->b = 20;

指针使用示例:

struct A{
	int a;
	int b;
};

struct A array[10] = { 0 };
p = array;
p->a = 1;
p->b = 2;
p++;
p->a = 3;
p->b = 4;

也可以在堆中申请内存创建数组:

struct A{
	int a;
	int b;
};
p = malloc(sizeof(struct A) * 10);
struct A *array = p;

p->a = 1;
p->b = 2;
p++;
p->a = 3;
p->b = 4;

free(array);

7.当结构体中有指针,不能直接给结构体内的指针多次赋值,多次赋值要申请内存
struct str{
	char *name;
	int age;
};

int main(){
	struct str st = { NULL,0 };
	st.age = 30;
	st.name = malloc(100);
	strcpy(st.name, "吴志刚");
	printf("%d,%s\n", st.age, st.name);
	free(st.name);
	return 0;
}
8.结构体作为参数:
struct st{
	char name[1000];
	int age;
};
void print_student(struct student s){
	printf("name=%s,age=%d\n", s.name, s.age);
}
void set_student(struct student s,const char *name,int age){
	strcpy(s->name, name);
	s->age = age;
}
int main(){

	struct student st = { "tom",20 };
	set_student(&st, "mike", 100);
	print_student(st);  //结果打印出mike  100
	return 0;
}
定义一个结构体作为参数的时候,尽量使用指针,而不是使用结构变量,这样代码效率高

9.联合体:

联合union是一个能在同一个存储空间存储不同类型数据的类型。

联合体所占的内存长度等于其最长成员的长度,也有叫做共用体。

联合体虽然可以有多个成员,但同一时间只能存放其中一种。

对于联合体来讲最基本的原则是,一次只操作一个成员变量,如果这个变量是指针,那么一定是处理完指针对应的内存之后再来使用其他成员。


    unionvariant{

       int ivalue;

       char cvalue;

       double dvalue;

    };

 

    int main()

    {

       union variant var;

       var.cvalue = 12;

       printf("%d\n", var.ivalue);

       printf("%p, %p, %p\n", &(var.cvalue), &(var.ivalue), &(var.dvalue));

       return0;

   }
















三.枚举类型

可以使用枚举(enumeratedtype)声明代表整数常量的符号名称,关键字enum创建一个新的枚举类型。

实际上,enum常量是int类型的。

枚举的本质就是int型的常量。

    enumspectrum {red,yellow,green,blue, white, black };

    enumspectrum color;

    color = black;

    if (color != red)


1.默认值

默认时,枚举列表中的常量被指定为0,1,2等

    enumspectrum {red,yellow,green,blue, white, black };

    printf("%d, %d\n",red,black);

指定值

可以指定枚举中具体元素的值

    enumspectrum {red =10,yellow =20,green,blue, white, black };

    printf("%d, %d\n",red,black);


四.typedef:

typedef是一种高级数据特性,它能使某一类型创建自己的名字

typedef unsigned char BYTE

1.与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值

2.typedef是编译器处理的,而不是预编译指令

3typedef比#define更灵活

直接看typedef好像没什么用处,使用BYTE定义一个unsigned char。使用typedef可以增加程序的可移植性。

4.通过typedef定义函数指针

typedefconstchar *(*SUBSTR)(constchar *,const char *);

constchar *getsubstr(constchar *src,constchar *str)

{

   return strstr(src,str);

}

const char *func(const char *(*s)(constchar *,constchar *), const char *src,constchar *str)

const char *(*p[3])(constchar *,constchar *);

在程序当中如果是定义一个可读的常量,适合用#define

如果定义的是一个具体的数据类型,那么typedef更加适合。

如果是定义一个函数指针,那么基本就typedef吧.


五.文件操作

1.不论操作什么类型的文件,第一步先打开一个文件,第二步,读写文件,第三步关闭文件。

2.fopen

r 以只读方式打开文件,该文件必须存在。

r+ 以可读写方式打开文件,该文件必须存在。用r+写文件时候,从文件开始位置写入

rb+ 读写打开一个二进制文件,允许读写数据,文件必须存在。

rw+ 读写打开一个文本文件,允许读和写。

w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。

wb

w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。

a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留),如果文件不存在,a的行为和w是一样的

a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)

“b”只对windows有效,对于unix来讲是无效,

3.文件操作helloworld案例:

写入文件

int main(){

	char s[1024] = { 0 };
	FILE *p = fopen("D:\\temp\\a.txt", "w");//用写的方式打开一个文件
	//w意思就是如果文件不存在,就建立一个,如果文件存在就覆盖
	while(1){
		memset(s, 0, sizeof(s));
		gets(p);
		if (strcmp(s, "exit") == 0)
			break;
		int len = strlen(s);
		s[len] = '\n';
		fputs(s, p);
	}
	fclose(p);//关闭这个文件
	printf("end\n");
	return 0;
}

读出文件:
int main() {

	char s[1024] = { 0 };
	FILE *p = fopen("D:\\temp\\a.txt", "r");//用读的方式打开一个文件
	while (!feof(p)) {						//feof(p)如果已经到了文件结尾,函数返回真
		memset(s, 0, sizeof(s));
		fgets(s, sizeof(s), p);
		printf("%s", s);
	}
	fclose(p);//关闭这个文件
	return 0;
}
4.二进制和文本模式的区别
——在windows系统中,文本模式下,文件以"\r\n"代表换行。若以文本模式打开文件,并用fputs等函数写入换行符"\n"时,函数会自动在"\n"前面加上"\r"。即实际写入文件的是"\r\n" 。
——在类Unix/Linux系统中文本模式下,文件以"\n"代表换行。所以Linux系统中在文本模式和二进制模式下并无区别。
对于GBK编码的汉字,一个汉字两个字节,对于utf8来讲一个汉字3个字节,但如果英文字母都是一个字节

5.getc和putc函数

int main()

{

    FILE *fp = fopen("a.txt", "r");

    char c;

    while ((c = getc(fp)) !=EOF)

    {

       printf("%c", c);

    }

    fclose(fp);

    return0;

}

int main()

{

    FILE *fp = fopen("a.txt", "w");

    constchar *s ="hello world";

    int i;

    for (i =0; i

    {

       putc(s[i], fp);

    }

    fclose(fp);

    return0;

}


6.EOF与feof函数文件结尾

程序怎么才能知道是否已经到达文件结尾了呢?EOF代表文件结尾
如果已经是文件尾,feof函数返回true。

7.fprintf,fscanf,fgets,fputs函数(都是操作文件的一行,不是单个字符)

这些函数都是通过FILE*来对文件进行读写。

——fprintf:功能和printf一样,fprintf是把数据输出到文件中,fprintf(p,"%s" ,buf);  p是要输出的文件,buf是输出的值

——fscanf与scanf用法基本一致,fscanf是从一个文件读取输入,scanf是从键盘读取输入,用法:fscanf(p , "%s"  ,buf);第一个参数是获取文件内容的指针,第二三个参数与scanf相同,fscanf不会读取行尾的’\n’,

——fgets会将行尾的’\n’读取到buf里面,用法:

int main() {

	
	FILE *p = fopen("D:\\temp\\a.txt", "r");//用读的方式打开一个文件
	while (!feof(p)) {//feof(p)如果已经到了文件结尾,函数返回真
		
		char s[1024] = { 0 };
		memset(s, 0, sizeof(s));
		fgets(s, sizeof(s), p);  //通过p读取文件内容赋值给s,第二个参数控制读取数据多少
		printf("%s", s);
	}
	fclose(p);//关闭这个文件
	return 0;
}

不论fprintf还是fputs都不会自动向行尾添加\n,需要代码中往buf的行尾写\n才可以达到换行的目录

8.stat函数

#include

函数的第一个参数代表文件名,第二个参数是structstat结构。

得到文件的属性,包括文件建立时间,文件大小等信息。

9.fseek函数

函数设置文件指针stream的位置。如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。如果执行失败则不改变stream指向的位置,函数返回一个非0值。

实验得出,超出文件末尾位置,还是返回0。往回偏移超出首位置,还是返回0,请小心使用。

第二个参数负数代表向前移动,整数代表向后移动。

第一个参数stream为文件指针

第二个参数offset为偏移量,单位:字节,正数表示正向偏移,负数表示负向偏移

第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET

SEEK_SET: 文件开头

SEEK_CUR: 当前位置

SEEK_END: 文件结尾

fseek(fp, 3, SEEK_SET);


10.ftell函数

函数ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁的前后移动,程序不容易确定文件的当前位置。

longlen=ftell(fp)

11.fgetpos,fsetpos函数

fseek与ftell返回的是long类型,如果文件很大,超过long的范围,那么该函数会有问题,fgetpos与fsetpos函数可以处理更大的文件类型

返回值:成功返回0,否则返回非0

    fpos_t ps = 0;

    fgetpos(fp, &ps);

    fpos_t ps = 2;

    fsetpos(fp, &ps);

12.fflush函数
fflush函数可以将缓冲区中任何未写入的数据写入文件中。
修改配置文件,希望修改实时生效,那么每次修改完成之后我们fflush一次
技术分享

13.fread和fwrite函数
size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
注意:这 个函数以二进制形式对文件进行操作,不局限于文本文件
返回值:返回实际写入或读取的数据块数目
只要读取到文件最后,没有完整的读取一个数据块出来,fread就返回0
第一个参数代表void *,写入或者读取的缓冲区
第二个参数是代表写入或读取的时候最小单位的大小
第三个参数是代表写入或读取几个单位
第四个参数是FILE *

14.fread与feof

注意以下两段代码的区别

   while (!feof(p))

   {

      fread(&buf, 1, sizeof(buf), p);

   }

while (fread(&buf,1,sizeof(buf), p))


15.从一个文件读出数据,然后对其排序,又写入另一个文件:

#include 
#include 
#include 

void swap(int *a, int *b)//交换参数的值
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

void pupple(int *p, int n)//冒泡排序
{
    int i;
    int j;
    for(i = 0; i p[j])
            {
                swap(&p[j - 1], &p[j]);
            }
        }
    }
}

int main(void)//在堆建立一个数组对文件内容进行排序
{
    int index = 0;//这是个计数器
    char buf[100];

    FILE *p = fopen("D:\\temp\\a.txt", "r");//第一次打开a.txt目的是要知道这个文件有多少行
    while(!feof(p))//如果没有到达文件结尾,那么循环继续,第一次循环的时候并不是要处理文件的内容,只是统计文件的行数
    {
        memset(buf, 0, sizeof(buf));//每次读取文件一行之前都把这个buffer清空
        fgets(buf, sizeof(buf), p);//从文件中读取一行
        index++;
    }
    fclose(p);

    int *array = calloc(sizeof(int), index);//在堆中建立一个动态数组,动态数组的成员数量和a.txt文件的行一样多

    p = fopen("D:\\temp\\a.txt", "r");
    index = 0;//计数器从0重新开始
    while(!feof(p))//如果没有到达文件结尾,那么循环继续
    {
        memset(buf, 0, sizeof(buf));//每次读取文件一行之前都把这个buffer清空
        fgets(buf, sizeof(buf), p);//从文件中读取一行
        array[index] = atoi(buf);//将读取到的一行转化为int,赋值给数组成员
        index++;
    }
    fclose(p);
    pupple(array, index);//将数组排序
    p = fopen("D:\\temp\\b.txt", "w");//用写的方式打开b.txt
    int i;
    for(i = 0; i 

c提高


推荐阅读
  • [c++基础]STL
    cppfig15_10.cppincludeincludeusingnamespacestd;templatevoidprintVector(constvector&integer ... [详细]
  • LDAP服务器配置与管理
    本文介绍如何通过安装和配置SSSD服务来统一管理用户账户信息,并实现其他系统的登录调用。通过图形化交互界面配置LDAP服务器,确保用户账户信息的集中管理和安全访问。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • 自定义滚动条美化页面内容
    当页面内容超出显示范围时,为了提升用户体验和页面美观,通常会添加滚动条。如果默认的浏览器滚动条无法满足设计需求,我们可以自定义一个符合要求的滚动条。本文将详细介绍自定义滚动条的实现过程。 ... [详细]
  • importpymysql#一、直接连接mysql数据库'''coonpymysql.connect(host'192.168.*.*',u ... [详细]
  • 微软推出Windows Terminal Preview v0.10
    微软近期发布了Windows Terminal Preview v0.10,用户可以在微软商店或GitHub上获取这一更新。该版本在2月份发布的v0.9基础上,新增了鼠标输入和复制Pane等功能。 ... [详细]
  • Framework7:构建跨平台移动应用的高效框架
    Framework7 是一个开源免费的框架,适用于开发混合移动应用(原生与HTML混合)或iOS&Android风格的Web应用。此外,它还可以作为原型开发工具,帮助开发者快速创建应用原型。 ... [详细]
  • 本文介绍了如何使用 CMD 批处理脚本进行文件操作,包括将指定目录下的 PHP 文件重命名为 HTML 文件,并将这些文件复制到另一个目录。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 两个条件,组合控制#if($query_string~*modviewthread&t(&extra(.*)))?$)#{#set$itid$1;#rewrite^ ... [详细]
  • 本文详细介绍了DMA控制器如何通过映射表处理来自外设的请求,包括映射表的设计和实现方法。 ... [详细]
  • 解决Win10下MySQL连接问题:Navicat 2003无法连接到本地MySQL服务器(10061)
    本文介绍如何在Windows 10环境下解决Navicat 2003无法连接到本地MySQL服务器的问题,包括启动MySQL服务和检查配置文件的方法。 ... [详细]
  • 本文详细介绍了如何利用Duilib界面库开发窗体动画效果,包括基本思路和技术细节。这些方法不仅适用于Duilib,还可以扩展到其他类似的界面开发工具。 ... [详细]
  • Spark中使用map或flatMap将DataSet[A]转换为DataSet[B]时Schema变为Binary的问题及解决方案
    本文探讨了在使用Spark的map或flatMap算子将一个数据集转换为另一个数据集时,遇到的Schema变为Binary的问题,并提供了详细的解决方案。 ... [详细]
  • 第二十五天接口、多态
    1.java是面向对象的语言。设计模式:接口接口类是从java里衍生出来的,不是python原生支持的主要用于继承里多继承抽象类是python原生支持的主要用于继承里的单继承但是接 ... [详细]
author-avatar
乌桥老鹅
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有