C语言Part03

区分指针运算
*s++ (*s)++
前者是指 指针S指向的地址进行偏移
后者是指 指针S指向的地址里存放的数据进行自增
string.h中的函数
#include<string.h>
// 在str所指的字符串的前n个字节中搜索第一次出现字符C的位置,并返回其内存地址,如果没有找到就返回NULL
void *memchr(const void *str, int c, size_t n);void *memchr(const void *str, int c, size_t n){const char *s = (const char *)str;size_t i;for(i=0;i<n;i++){if(s[i] == c){return str+i;}}return NULL;
}//把 str1 和 str2 的前 n 个字节进行比较   相等返回0int memcmp(const void *str1, const void *str2, size_t n);
int memcmp(const void *str1, const void *str2, size_t n){const char *s1 = (const char *)str1;const char *s2 = (const char *)str2;for(size_t i=0;i<n;i++){if(s1[i] < s2[i]){return -1;}else if(s1[i] > s2[i]){return 1;}else{}}return 0;
}//从 src 复制 n 个字节到 dest。   内存拷贝函数
void *memcpy(void *dest, const void *src, size_t n);
float a = 3.375;
int m = a; //m = 3
memcpy(&m,&a,4);  //m = 0x40580000
void *memcpy(void *dest, const void *src, size_t n){char *pd = (char *)dest;const char *ps = (const char *)src;for(size_t i=0;i<n;i++){pd[i] = ps[i];}return dest;
}//另一个用于从 src 复制 n 个字节到 dest 的函数
//但是在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。
void *memmove(void *dest, const void *src, size_t n);//复制字节 c(一个无符号字符)到参数 str 所指向的内存的前 n 个字节。
//所有的函数 int c 但实际上只使用c整数一个字节(最低的字节)
void *memset(void *str, int c, size_t n);//在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。 如果没有返回NULL
char *strchr(const char *str, int c);char *strchr(const char *str, int c){while(*str!='\0' && *str != c){++str;}if(*str == c){return str;}return NULL;
}//在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。  r:reverse 逆序
char *strrchr(const char *str, int c);//如果没有找到返回NULL//把 str1 和 str2 进行比较,结果取决于 LC_COLLATE 的位置设置。 "按拼音顺序来比较"  "中国" "中共"
int strcoll(const char *str1, const char *str2);
//根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。
size_t strxfrm(char *dest, const char *src, size_t n);//检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。
//从str1中找出在str2中任意一个字符第一次出现在str1中下标位置  没找到,返回str1字符串长度
//不会把str2看作一个整体  相当于是一个字符集合
size_t strcspn(const char *str1, const char *str2);//检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标。
size_t strspn(const char *str1, const char *str2);//检索字符串 str1 中第一个匹配字符串 str2 中字符的字符,不包含空结束字符。也就是说,依次检验字符串 str1 中的字符,当被检验字符在字符串 str2 中也包含时,则停止检验,并返回该字符位置。 如果str1中不包含str2中的字符,则返回NULL
char *strpbrk(const char *str1, const char *str2);//从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。
//对于C语言标准库或者系统调用函数,对于出错的情况,基本上都会设置全局变量errno,可以通过strerror函数获取错误信息
//用不同的整数  代表不同的错误信息    strerror函数就可以通过整数(错误码)获取错误信息
char *strerror(int errnum);    //基本上会结合 errno 使用//在字符串 haystack 中查找第一次出现字符串 needle(不包含空结束字符)的位置。 如果不存在则返回NULL
//查找子串的位置
char *strstr(const char *haystack, const char *needle);  //如果以朴素的思想来实现很简单  KMP//分解字符串 str 为一组字符串,delim 为分隔符。  会把str中delim字符串的字符更改为'\0'
char *strtok(char *str, const char *delim);
//返回拆分之后第一个字符串   后面每调用一次 strtok(NULL,delim) 都会返回下一个分组的字符串 直到NULL
ctype.h中的函数
函数名 如果是下列函数时,返回值为真
isalnum(int c) 字母或者数字
isalpha(int c) 字母
isblank(int c) 标准的空白字符(空格、水平制表符或者换行)或者任何其他本地化指定为空白符的字符
iscntrl(int c) 控制字符,如ctrl+B ox7f以上的
isdigit(int c) 数字
isgraph(int c) 除空格之外的任意可打印字符
islower(int c) 小写字母
isprint(int c) 可打印字符
ispunct(int c) 标点符号(除空格或字母数字字符以外的任何可打印字符)
isspace(int c) 空白字符
isupper(int c) 大写字母
isxdigit(int c) 十六进制数字符

ctype.h头文件中的字符映射函数

函数名 行为
tolower(int c) 如果参数是大写字符,该函数返回小写字符;否则,返回原始参数
toupper(int c) 如果参数是小写字符,该函数返回大写写字符;否则,返回原始参数
#include <stdio.h>
//把字符输出到stream文件中      stream如果取stdout 则表示输出到控制台
int fputc(int c, FILE *stream);
//把字符串输出到stream文件中    stream如果取stdout 则表示输出到控制台
int fputs(const char *s, FILE *stream);
//和fputc一样
int putc(int c, FILE *stream);
//输出一个字符
int putchar(int c);
//输出一个字符串 自动的换行
int puts(const char *s);//从文件stream中读取一个字符    stream取stdin 表示从标准输入读取(控制台)
int fgetc(FILE *stream);
//从stream读取最多size-1个字符到s缓存中  一定保证有'\0'
char *fgets(char *s, int size, FILE *stream);
//和fgetc一样
int getc(FILE *stream);
//从标准输入中读取一个字符
int getchar(void);
//往输入流中放入一个字符c
int ungetc(int c, FILE *stream);char *gets(char *s); //Never use this function.
字符串格式化IO
//遵循格式字符串 format 中的格式占位符
int scanf(const char *format, ...);          //格式化标准输入    是从控制台读取
int fscanf(FILE *stream, const char *format, ...);  //文件格式化标准输入  是从文件stream中读取
int sscanf(const char *str, const char *format, ...); //字符串格式化标准输入 从字符串str中读取int printf(const char *format, ...);   //格式化标准输出     输出到控制台
int fprintf(FILE *stream, const char *format, ...); //文件格式化标准输出  输出到文件stream
int sprintf(char *str, const char *format, ...); //字符串格式化标准输出  输出到字符串str
int snprintf(char *str, size_t size, const char *format, ...); //字符串格式化标准输出
//指定了str最大存储空间为size个字节//返回值  都 表示正确读取成功的次数(按占格式占位符)
//          表示成功写入的字节数

IO

标准输入scanf

  • 格式化标准输入

    int scanf(const char * fomat,…);

    scanf是标准输入缓冲区中按照格式字符串进行读取数据,如果缓冲区中没有数据,则会等待用户输入数据,弱国缓冲区有数据,则会直接读取不会等待用户输入

    用户输入时,会一口气把用户所有的输入包括ENTER 一起读入缓冲区中

    一般的格式占位符(读取数据类型的)会忽略空白字符以及换行

    而%c则不会忽略缓冲区中任何数据,导致读取换行

    get fgets都会读取\n

可以手动清除缓冲区内的数据

scanf("%*[^\n]")//从缓冲区中读取除了\n以外所有的字符并丢掉
scanf("%*c");从缓冲区读取任意一个字符
  • 标准输出

    //表示成功输出的字节数
    int printf(const char *format, ...);%+-m.nd       %格式占位符标识 +-  左右对齐m   占字符宽度.   小数点n   小数部分宽度
    %#      前缀
    
    • 标准输出是输出到输入缓冲区,只有满足一定的条件时,输出缓冲区的内容才会输出到屏幕(控制台)

      • 换行 \n
      • 缓冲区满了 4kb
      • 强制刷新缓冲区 fflush(stdout);
      • IO转换 printf() 如果调用 scanf()
      • 程序结束

可变长参数

#include <stdarg.h>void va_start(va_list ap, last);//last 是最后一个有名参数
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
  • 定义可变长参数列表时,至少有一个是确定的参数,为了能够解释出来实参

    ISO C requires a named argument before ...
    
  • 自定义可变长参数函数

    RET_TYPE funcname(TYEP argname, ...){//argname一般用于告知 ... 中有多少个参数 以及每一个参数的类型  scanf  printf//1.定义变量va_list ap;  //ap这样的变量不能直接赋值   va_list ap1;  //2.初始化va_start(ap,argname);  //va_copy(ap1,ap);    相当于赋值  不能直接=//3.每调用一次 获取一个参数type arg = va_arg(ap,type);   //type指定参数的类型//4.释放ap变量va_end(ap);
    }
    
  • 在调用可变长参数函数时,除了有名(带名字的参数)参数以外,其他(…)可以传递任意多个任意类型的参数

  • scanf/printf第一个有名参数 const char *format(格式字符串)

    • 格式字符串告诉了scanf/printf函数后面传递了多少个参数,每个参数的类型

const

const修饰变量 表示该变量只读 “理论上不可以修改”

如果直接对const修饰的变量进行修改会报错

const修饰的变量虽然只读 但是不能当作常量

const和指针

大部分情况下 const都是和函数的形参列表中和指针结合使用

这样是为了保护在函数中修改实参的值

const的位置不同修饰的内容也不同

const char*s1; 指针内存地址中的数据只读 常量指针
char * const s1;指针内存地址只读 不可改变指向  指针常量
const char * const s1; 即数据只读 也内存地址只读

const的作用

1.修饰变量 表示只读

2.在函数形参列表使用 防止实参被修改

注意:

当const type * 直接赋值给 type *时会有警告,可以用强制转换消除

const type a; &a 类型为 const type *

动态内存

栈内存 变量自动申请内存 自动释放

动态内存 堆内存 手动申请 手动释放

#include <stdlib.h>
/*
函数功能: 向操作系统申请size个字节大小的内存空间
返回值: void * 成功返回size个字节大小的内存空间起始位置失败返回NULL
*/
void *malloc(size_t size);
/*
函数功能: 向操作系统申请nmemb个size字节大小的连续内存空间   nmemb*size个字节的内存空间  并初始化数据为"0"
返回值:  void *:  万能指针  作为nmemb*size个字节的内存空间的起始位置失败返回NULL
参数:size:  单位内存大小nmemb: 单位内存的个数//int arr[5]  5个int类型的内存空间
*/
void *calloc(size_t nmemb,size_t size);//函数可以于malloc(nmemb*size)
/*
函数功能:realloc调整之前申请的动态内存ptr的大小为size个字节并且返回调整之后内存的起始位置
注意:调整内存大小可能会改变内存的位置,也有可能不会改变因此,一定要接收realloc函数的返回值ptr是之前通过malloc/calloc/realloc函数返回的内存地址,通过realloc之后,自动把内存中的数据拷贝到新内存中,并且返回新内存的起始位置
*/
void *realloc(void *ptr,size_t size);
/*
函数功能:free释放动态内存释放malloc/calloc申请的动态内存在程序中如果没有释放malloc/calloc申请的动态内存,则该内存会一直存在,造成内存泄漏只能释放动态内存地址,不能释放栈内存,全局数据区内存  试图free一个非堆内存地址(程序崩溃,核心已转储)如果free时释放的是malloc/calloc返回的起始位置的偏移位置,则一样是程序崩溃,核心已转储同一个动态内存不能多次free,如果多次free,则运行崩溃(double free)核心已转储
*/
void free(void *ptr);

realloc函数可以对之前申请的内存进行扩容和缩容

1.原内存后的内存是空闲的,则会直接在此基础上进行扩充

2.原内存后内存已经被使用,则会申请一片新的内存,将原先的内存自动释放,并返回新内存的起始地址

申请动态内存时要申请几个字节就是用几个字节,不要越界使用,这是因为即便用户申请的内存很小,小于24字节,系统还是会分配24字节给用户,但这片内存中有着至少八个字节(64位)的内存时用来控制用户申请的这片内存,其记录了内存的大小,起始位置,是否空闲等等

用户在第一次申请内存时,系统会直接分配至少33页的动态内存,一页动态内存是4096个字节

我们申请的内存都是虚拟内存,系统会将物理内存和我们的虚拟内存进行映射,以此来使用。

释放动态内存只是将该内存标记为闲置状态,只有当该内存后所有的内存处于空闲状态,才会进行页回收 也就是取消映射。

申请动态内存时会失败的,失败时返回NULL

由于操作系统不知道申请的内存存储的数据类型所以返回的是void*类型的地址 用户接受时需要进行强转,将其转化为自己数据的类型。主要是因为void *不可以解引用

申请的动态内存是连续的空间,所以可以像数组一样用下标[]访问每个数据

内存碎片

频繁申请和释放内存,会产生内存碎片。

申请1字节内存,至少会分配24字节,在使用过程,只能使用1byte,剩下的23byte就是内存碎片

在申请n字节内存空间时,有一块m字节的内存块是处于空闲的(m>n),操作系统会把这一块m字节的内存块分配给用户使用,而用户只能使用n个字节,剩下m-n个字节就成为了内存碎片

内存泄漏

申请动态内存后没有释放

内存泄漏会导致程序运行越来越卡,如果内存不够,最后崩溃

所以,使用动态内存最好是在需要大量内存的情况下申请。

关于函数返回指针

函数返回指针,也就是返回一个内存地址,

局部变量会自动申请释放内存,所以不能返回局部变量的内存地址

可以返回存储在动态内存中的变量,因为动态内存需要手动释放,而且是在返回后释放

可以返回函数的实参

可以返回全局变量或静态变量的内存地址

可以返回NULL

变量

变量的分类

根据变量定义的位置 将变量分为全局变量,局部变量,块变量

作用域 取决于定义的位置

存储位置 取决于定义的位置和 关键字static register的修饰

生命周期 取决于存储位置

全局变量

定义在全局域(函数外部)

在本文件任何函数内都可以使用

是否可以在其他文件中访问 取决于全局变量是否是静态变量和是否用extern声明

静态全局变量只能在本文件内使用

普通全局变量可以在其他文件通过extern进行声明并访问

全局变量的生命周期:从程序运行到结束

全球变量的存储位置:全局数据区(数据段)

全局变量如果没有初始化,自动初始化为零;

局部变量

定义在函数内部

作用域:只能在函数内部使用

静态局部变量static

​ 生命周期:从程序开始到结束

​ 程序运行时就会为静态变量在全局数据区分配内存空间,等到函数调用时不会再执行静态变量的定义语句

​ 存储位置:全局数据区

普通局部变量:

​ 生命周期:函数调用期间 调用时才会申请内存,函数调用结束自动释放内存

​ 存储位置:栈

局部变量可以和全局变量同名,但在函数内调用时会优先访问局部变量,只有用extern才能访问到全局变量

块变量

定义在语句块中的变量

作用域:只作用于当前语句块

生命周期:

普通块变量:语句块执行期间

静态块变量:程序运行期间

存储位置:

普通块变量:栈

静态块变量:全局数据区

变量修饰符

auto

自动的 在c语言中 定义的变量默认就是atuo 一般不用 可以省略

static

静态的

static修饰局部变量 改变了存储位置和生命周期

static定义变量的语句,只会执行一次

程序启动时,为static变量分配内存,并执行初始化语句

static修饰的变量,在程序运行期间,不管函数调用多少次,内存地址不变

普通的变量,每次调用函数,其内存地址都有可能发生变量

普通局部变量如果没有初始化,垃圾值,值是不确定的

static修饰的局部变量,即使没有初始化,也会自动初始化为"零" 0

static修饰的变量,如果要初始化,必须初始化为常量

static修饰全局变量 改变了作用域

static修饰函数

限制了函数的作用域,表示函数只能在本文件中调用

在多文件编程时,头文件中,一般只放函数的声明,但是对于static修饰的函数,静态函数的定义可以放在头文件中

register

寄存器

请求编译器把register修饰的变量作为寄存器变量

register修饰的变量是没有内存地址的,是因为寄存器变量存储在寄存器上面(寄存器不属于虚拟内存的)

register变量不能进行取址运算(&)

如果一个变量被频繁读写,为了提高效率,直接可以把变量作为寄存器变量,以提高运行效率,CPU在处理register变量时,不需要从内存中加载,也不需要把其结果写回内存

register变量其实受到很多限制

个数限制 寄存器数量有限

寄存器字长度 不是任意类型数据都可以作为寄存器变量 机器字长

数据存储

硬盘 固态硬盘 内存 高速缓存(Cache) 寄存器

从左往右 容量是越来越小 造价是越来越贵 IO效率越来越快

程序存储在磁盘上 启动时 从磁盘上把数据加载到内存中 程序执行代码期间 把数据从内存中加载到Cache再到寄存器 操作系统操作的是寄存器

volatile
  • 易变的

  • volatile修饰变量,表示该变量可能随时发生意外的变化

    • 在使用volatile变量时,不能直接使用寄存器的值,需要重新从内存中加载进来
volatile int a = 10;//如果要计算a的平方
int s = a*a;//10*10  第一次加载进行的可能是10,但第二次加载进来的可能就不是10int b = a; //加载
s = b*b;
const

const修饰局部变量,表示局部变量只读,不会改变局部变量的存储位置

const修饰全局变量,表示全局变量只读,改变了全局变量的存储位置

const修饰的全局变量,存储在字面值常量区

内存布局

程序(进程)内存布局

内存地址 实际上是虚拟内存

每个程序都有独立的虚拟内存

32位 0x0000 0000 -0xffffffff 有4G虚拟内存地址 3G为用户区 1G为内核区

64位 0x0000 0000 0000 0000-0xffff ffff ffff ffff

理论上有2^34G 实际上只有256T 2^48 而不是 2^64

内核和用户各自占一半

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ocoxXR0c-1683980959134)(C:\Users\asus\Desktop\新版笔记\C语言day11\images\64bit256T虚拟内存.png)]

虚拟内存需要映射到物理内存才能使用

因此出现段错误的原因有以下两点

1.访问没有权限的内存地址

2.访问没有经过映射的虚拟内存

编译

预处理 gcc -E xxx.c -o xxx.i 对xxx.c进行预处理 并生成 xxx.i文件

预处理会处理#include的指令将头文件的内容导入

预处理会删除宏 进行宏替换

预处理与进行条件编译

预处理会删除用户的注释

会检查#pragma GCC 指令

编译

gcc -S xxx.c/h /i cc以前的指令

自动生成xxx.s文件

编译阶段会进行语法语义的检查 通过后生成 汇编文件

汇编

gcc -c xxx.c/h/s/i/ as以前的指令

自动生成.o文件 目标文件

汇编会将汇编代码转化为机器指令 二进制

.h 会变成 .h.gch

链接

gcc x.o/s/c/h ld以前的指令 加上 -o可以生成指定名字的文件

链接目标文件和库文件 生成可执行程序

C语言学习Part03相关推荐

  1. C++语言学习(十二)——C++语言常见函数调用约定

    C++语言学习(十二)--C++语言常见函数调用约定 一.C++语言函数调用约定简介 C /C++开发中,程序编译没有问题,但链接的时候报告函数不存在,或程序编译和链接都没有错误,但只要调用库中的函数 ...

  2. 微软提出CLIPBERT:通过稀疏采样的视频语言学习

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 论文是学术研究的精华和未来发展的明灯.小白决心每天为大家带来经典或 ...

  3. c语言错误的等式,C语言学习中几个常见典型错误分析.docx

    C语言学习中几个常见典型错误分析 打开文本图片集 摘要:C语言是一门优秀,应用广泛的结构化程序设计语言,是中职计算机.机电和电子技术等专业一门理论和实践相结合的课程,教学实践中,学生常觉得c语言难学, ...

  4. 二级C语言学习宝典下载,二级C语言学习宝典

    二级C语言学习宝典app是一款专注于全国二级计算机等级C语言学习所开发的应用软件,它能够给你带来全新的功能板块,让你轻松掌握超多优质的考试训练,享受到手机端做题刷题的畅快体验,让你轻松备考,而且在该软 ...

  5. php7做了哪些优化,PHP语言学习之PHP7做了哪些优化

    本文主要向大家介绍了PHP语言学习之PHP7做了哪些优化,通过具体的内容向大家展示,希望对大家学习php语言有所帮助. 一  zval使用栈内存 在Zend引擎和扩展中,经常要创建一个PHP的变量,底 ...

  6. C语言学习趣事_之_大数运算_加法

    C语言学习趣事_大数运算_之加法 1.引子    在C语言中,因为预定义的自然数类型的大小是有上下限度的,这就决定了在进行数的运算的时候,必然受到限制,同时因为C语言是最接近汇编的一种程序设计语言,并 ...

  7. 攻破c语言笔试与机试难点,如何攻破C语言学习、笔试与机试的难点.doc

    如何攻破C语言学习.笔试与机试的难点 第一节??C语言编程中的几个基本概念1.1? ?? ?#include< >与#include" "? 1.? ?#include ...

  8. c语言 浮点型数据怎么存放,C语言学习之浮点型数据存储

    C语言学习之浮点型数据 浮点数 浮点型数据分为单精度浮点型(float)和双精度浮点型(double). 单精度(float) 单精度浮点值 取值范围:1.2E-38 到 3.4E+38 精度:6 位 ...

  9. r语言electricity数据集_R语言学习10-查看数据

    当我们处理一个新的数据集的时候,第一件事就是要对数据做一个了解.数据的格式是什么?数据的维度是多少?变量名是什么? 变量如何存储? 是否缺少数据? 数据中是否有任何缺陷? 本次课将学习如何使用R的内置 ...

最新文章

  1. 如何解决Spring Data Maven构建的“生命周期配置未涵盖的插件执行”
  2. Flex 学习笔记------as 与 js 的通信
  3. android 中组件继承关系图,一目了然
  4. Windows Store Javascript项目使用高德地图、谷歌地图、百度地图API
  5. windows安装软件最好使用独立的文件夹
  6. html5 java 微信商城_微信商城和H5商城区别是什么?
  7. 统计机器学习导论第四章答案
  8. C#判断是否是节假日
  9. manjaro 亮度调节
  10. Java中Map详解
  11. 计算机硬盘空间不足解决办法,3种方法解决Windows10硬盘空间不够的问题
  12. PHP拆分粘连的英文单词,英语单词拆分技巧
  13. jquery鼠标移入文字提示_Jquery hover鼠标经过时弹出div动态提示语
  14. 万能的ogg转换mp3格式的小技巧
  15. C++ 无法打开 源 文件「bits/stdc++.h」//E1696
  16. 前百度总裁陆奇:我给有梦想的年轻人9点建议
  17. 谷歌卫星地图下载器有哪些那款好用
  18. 解锁Bootloader
  19. 基于MQTT的RPC协议
  20. 谷歌chrome xp_在Google Chrome中管理Windows Live帐户

热门文章

  1. Java版:字母的大小写转换
  2. python什么时候用类设计_Python 类的设计与 Java 类的设计有何区别?
  3. python画一箭穿心
  4. Spring更简单的读取和存储对象
  5. 平方数及其相关思想(SDUT)
  6. 利用html+css制作个人简历
  7. 古代婚姻制度之六礼是在哪个时期流传下来的?六礼指的是什么?
  8. Vue实现简单计算器
  9. 分布式、集群、分布式服务框架
  10. 爱奇艺qsv视频文件怎么转为mp4的格式