C-study(十二)

存储类别

数据存储在内存

存储值占用的一块内存称为对象

对象可以存储一个或多个值,可以不存值,一定有对应的大小

确定存储类别之后,根据内存管理规则,自动选择作用域和存储期

外部存储类别缺陷:其他函数可能擅自篡改当前函数使用的变量
const修饰可避免被意外篡改
保护性程序设计的黄金法则:按需知道,尽量在函数内部解决函数的任务,只共享需要共享的变量

int entity =3;
/*entity 标识符,用来指定特定对象的内容
是软件指定硬件内存中对象的方式
提供存储在对象中的值 3*/
int *pt =&entity;
/*  pt 标识符 指定一个储存地址的对象
*  *pt和entity指向相同的对象
* 
* 指向对象的表达式称为左值
* entity和*pt 既是标识符也是左值
* ranks+2*entity既不是标识符(不是名称), 也不是左值(不指定内存位置上的内容)
* *(ranks+2*entity)是左值,指定特定内存位置的值, 即ranks数组的第7个元素 
*/
int ranks[10];//创建一个可容纳10个int类型元素的对象,每个元素是一个对象

const char * pc ="Behold a string literal!";
/*
字符字面量存储在内存,包含字符的数组是一个对象,每个字符也是一个对象,不可修改左值
const修饰 可指向其他,字符串不可修改
pc 存地址的对象,可以指向其他字符串,故可修改的左值
*pc 指向存储'B'的数据对象,不可修改左值
*/

属性

作用域

描述可访问标识符的区域

块作用域

块:花括号括起来的代码区域,函数体,复合语句
定义在块中的变量,函数形参,局部变量
可见范围:定义处到包含该定义的块末尾,内层块内定义的变量只有内层块可用
C99之前,块作用域变量声明在块开头,C99之后任意位置
C99之后复合语句即使没有花括号也算块,其中定义的变量作用域仅限复合语句

函数作用域

goto

函数原型作用域

函数原型的形参
可见范围:从形参定义处到原型声明结束
声明时只关心形参类型,只在变长数组中形参名有用

void use_a_VLA (int n, int m,ar[n][m] ) ;//方括号中必须使用在函数原型中已声明的名称。

文件作用域

变量定义在函数外面
从定义处到该定义所在的文件末尾可见
全局变量

预处理时:头文件+源代码文件=翻译单元,被编译器认为是一个文件

文件作用域实际可见整个翻译单元

链接

无链接:具有块作用域、函数作用域、函数原型作用域的变量

具有文件作用域的变量可以是内部链接或外部链接
外部链接:可在多文件程序中使用,全局作用域\程序作用域
内部链接:一个翻译单元内使用 static修饰的文件作用域变量

int giants = 5;//文件作用域,外部链接
static int dodgers = 3;//文件作用域,内部链接

存储期

对象生存期
静态存储期 :程序执行期间一直存在,
文件作用域变量(内部链接static和外部链接),static修饰的块作用域变量

自动存储期:块作用域变量,局部变量 (未被static修饰)
进入块时分配,退出块时释放,
自动变量占用的内存相当于可重复使用的工作区,函数结束后,变量占用的内存可存储下一个函数的变量

变长数组,从声明处到块末尾

动态分配存储期:malloc分配,free释放

void more(int number)(
int index;//和number,每次调用时创建,离开时销毁
static int ct = 0;
/*
static修饰局部变量,静态:一直存在,
局部:只能由当前函数调用,
可给其他函数提供地址间接访问该对象,eg:指针形参 返回值*/
return 0;
)

线程存储期:被声明时到线程结束一直存在,
_Thread local 声明对象,每个线程都获得该变量的私有备份

动态分配存储期
在这里插入图片描述

自动变量

自动存储期:不会初始化,必须显式初始化(默认任意值)、进入块分配,退出块释放
块作用域:声明在块内或函数头的变量
无链接:只有在变量定义的块中通过变量名访问该变量

另一个函数使用的同名变量是存储在不同内存位置上的另一个变量

auto :存储类别说明符,默认既是auto,也可显式使用
嵌套块:仅限于该块及其包含的块使用
没有花括号的块

//C99 C11
int loop(int n)
{
	int m; //m的作用域,m和n作用于整个函数
	scanf ( "%d", &m);
	{
		int i; // m和 i 的作用域
		for (i = m; i <n; i++)
		puts ("i is local to a sub-block\n" ) ;
	}
	return m; //m的作用域,i 已经消失,此时i的内存可重复使用
}

int main ()
{
	int x = 30;//原始的x,必须初始化
	printf ( "x in outer block: %d at %p\n",x,&x);//30
	{
		int x =77;//新的x,隐藏了原始的x
		printf ("x in inner block : %d at %p\n",x,&x) ;//77
	}
	printf ("x in outer block : %d at %p\n", x,&x);//30
	while (x++<33)//原始的x 30-32
	{
		int x = 100;//新的x,隐藏了原始的,每次进循环体创建新的x=100
		x++;//递增新的x
		printf ( "x in while loop: %d at %p\n",x,&x);//101
	}
	printf ( "x in outer block : %d at %p\n", x,&x);//34


	int n = 8 ;
	printf("Initially,n =  %d at %p\n", n, &n) ;//8
	for (int n = 1; n < 3; n++)
		printf ("loop 1: n = %d at %p\n" , n, &n);//1-2 C99复合语句即使没有花括号也是块
	printf ( "After loop l, n = %d at %p\n", n, &n) ;//8
	for (int n = 1; n < 3; n++)
	{
		printf ( " loop 2 index n = %d at %p\n", n,&n);//1-2 循环头的n
		int n = 6;//每次循环重新定义
		printf ( "loop 2: n = %d at %p\n", n, &n) ;//6 循环体内的n、循环体是循环块的子块
		n++;
	}
	printf ("After loop 2, n = %d at %p\n", n, &n) ;//8

	return 0;
}

寄存器变量

存储在CPU的寄存器,存储在最快的可用内存,无法获取地址
块作用域无连接自动存储期
register:请求寄存器变量,由编译器决定,必须初始化
double不可声明为寄存器变量

静态变量

在内存中原地不动
在载入内存时执行初始化,默认初始化为0

块作用域的静态变量

块作用域、无链接、静态存储期的局部变量
程序离开所在函数,静态局部变量不会消失,只有当前函数可见
static修饰块中变量
函数形参不可使用static
内部静态存储类别 局部静态变量

#include <stdio.h>
void trystat (void) ;
int main (void)
{
	int count ;
	for (count = 1; count <= 3; count++)
	{
		printf ( "Here comes iteration %d : \n" ,count);
		trystat () ;
	}
	return 0;
}
void trystat (void)
{
	int fade = 1;//每次调用初始化,运行时行为
	static int stay = 1;
	//静态变量,只在编译时初始化一次,默认初始化为0
	printf ("fade = %d and stay = %d\n", fade++, stay++);//先打印、在递增
}

外部链接的静态变量

外部静态存储类别
文件作用域、外部链接、静态存储期
变量的定义放在函数外、且没有被static修饰
可用于同一程序的任意文件,其他文件使用时可用extern再声明

C99和C11识别局部标识符的前63个字符和外部标识符的前31个字符
以前的标准识别局部标识符的前31个字符和外部标识符的前6个字符

int Errupt;/*外部定义的变量,从声明处到文件结尾,默认初始化为0,只能用常量表达式初始化,只能在定义式声明中初始化一次

int x = 10;//没问题,10是常量
int y = 3 + 20;//没问题,用于初始化的是常量表达式
size_t z = sizeof (int) ;//没问题,用于初始化的是常量表达式
int x2 =2* x;不行,x是变量
(只要不是变长数组,sizeof表达式可被视为常量表达式。)
extern char ch='Y';//错误
*/
int tern = 1;//声明+定义,定义式声明,预留空间
double Up[100] ;/*外部定义的数组*/
extern char Coal;/*如果Coal 被定义在另一个文件,则必须这样声明,不会预留空间*/
void next(void);
int main(void)
{
	extern int Errupt;/*可省略,外部变量具有文件作用域,当前文件可直接使用
	(auto) int Errupt 独立的局部变量,仅main函数可见,隐藏外部定义的变量*/
	extern double Up[];/*可省略,不需要声明数组大小*/
	extern int tern;//再次声明,引用式声明
	printf ( "How many pounds to a firkin of butter?\n" );
	scanf ("%d",&Errupt);
	while (Errupt!=56)
		next();
	printf ("You must have looked it up! \n");

}
int Pocus;//main函数不可使用,声明在main后面
void next (void){
//可使用Errupt、Up、Pocus
printf ( "No luck,my friend. Try again .\n" ) ;
scanf ("%d", &Errupt) ;


}

内部链接的静态变量

文件作用域、内部链接、静态存储期
变量的定义放在函数外,用static修饰
只能用于当前文件
内部链接的静态变量

int traveler = 1;//外部链接,文件作用域,可用于其他翻译单元
static int stayhome = l; //内部链接
int main ()
{
extern int traveler; //重复声明具有文件作用域的travel
extern int stayhome;//重复声明具有文件作用域的stayhome,并未改变stayhome 的内部链接属性。

存储类别说明符

不可在声明时使用多个存储类别说明符,只有_Thread_local可以和static和extern一起使用

auto:自动存储期,只能用于块作用域的变量声明
register:寄存器类别,只能用于块作用域的变量声明,最快速度,无法获取地址
static:静态存储期,载入程序创建,程序结束消失
修饰文件作用域变量,作用限制在文件内,内部链接
修饰块作用域变量,作用限制在函数内,无链接
extern:变量的定义在别处,
修饰文件作用域变量时,变量必须有外部链接
修饰块作用域变量时,变量有外部链接或者内部链接
_Thread_local
typedef

多个文件时体现区别内部链接和外部链接的重要性
在一个文件中进行定义式声明,在其他文件中使用前必须引用式声明(extern)

//与partb.c一起编译
#include <stdio.h>
void report_count ();
void accumulate(int k);
int count = 0; //文件作用域,外部链接
int main (void)
{
	int value;//自动变量
	register int i; //寄存器变量
	printf ( "Enter a positive integer (0 to quit): ");
	while (scanf ( "%d", &value) == 1 && value > 0)
	{
		++count;//使用文件作用域变量
		for (i = value; i >= 0; i--)
			accumulate(i);
		printf ("Enter a positive integer (0 to quit) : ");
	}
	report_count();
	return 0;
)
void report_count()
{
	printf ("Loop executed %d times\n",count);
}


//partb.c --程序的其余部分
//与parta.c一起编译
#include <stdio.h>
extern int count;//引用式声明,外部链接
static int total = 0;//静态定义,内部链接
void accumulate(int k) ;//函数原型,没有调用,省略不影响使用
void accumulate(int k)// k具有块作用域,无链接
{
	static int subtotal = 0; //静态,无链接
	if(k<=0)
	{
		printf ("loop cycle: %d\n",count);
		printf ( "subtotal : %d; total: %d\n",subtotal,total) ;
		subtotal = 0;
	}
	else
	{
		subtotal += k;
		total += k;
	}
}

函数存储类别

外部函数:默认,可被其他文件函数访问,访问时必须extern修饰
静态函数:只能用于定义所在文件,其他文件可以使用同名函数
内联函数:C-study(十六)

随机数函数和静态变量

掷骰子:1-骰子面数 随机数

/*rand0.c --生成随机数 、使用ANSI C可移植算法*/
static unsigned long int next = 1;/*种子*/
unsigned int rand0 (void)
{/*生成伪随机数的魔术公式,0-32767之间的值,相同的种子得到的值相同*/
next = next * 1103515245 +12345;
return (unsigned int) (next / 65536)%32768;
}

/* r_drive0.c --测试rand0 ()函数、与rand0.c一起编译*/
#include <stdio.h>
extern unsigned int rand0 (void);
int main (void)
{
int count;
for (count = 0; count< 5; count++)
	printf("%d\n", rand0 ()) ;
return 0;
}


/* s_and_r.c --包含rand1()和srand1 ()的文件
使用ANSIC可移植算法*/
static unsigned long int next = 1;/*种子*/
int rand1 (void)
{/*生成伪随机数的魔术公式*/
next = next * 1103515245 + 12345;
return (unsigned int) (next / 65536)%32768;
}
void srand1 (unsigned int seed)
{
next = seed;
}

/* r_drive1.c --测试rand1 ()和srand1 () 与s_and_r.c 一起编译*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h> /*提供time ()的ANSI原型*/
extern void srand1 (unsigned int x);
extern int rand1 (void);
int main(void)
{
	int count;
	unsigned seed;
	printf ( "Please enter your choice for seed. \n");
	while (scanf ( "%u", &seed) == 1)
	{
	srand1(seed);/*重置种子*/
	for (count = 0; count < 5; count++)
		printf ("%d\n",rand1());
	printf ("Please enter next seed (q to quit) : \n");
	}
	printf ( "Done\n");
	srand1 ((unsigned int) time (0));/*根据系统时间自动初始化种子*/
	return 0;
}

/* diceroll.c --掷骰子模拟程序 与mandydice.c 一起编译*/
#include "diceroll.h"
#include <stdio.h>
#include <stdlib.h>/*提供库函数rand ()的原型*/
int roll_count = 0;/*外部链接 定义式声明*/
static int rollem (int sides)/*该函数属于该文件私有*/
{//sides 骰子面数
	int roll;
	roll = rand() % sides + 1;//取随机数,取模(0-5),+1(1-6)
	++roll_count;/*计算函数调用次数*/
	return roll;
}
int roll_n_dice(int dice,int sides)
{//sides面骰子掷dice次 点数和
	int d;
	int total = 0;
	if (sides < 2){
	printf ("Need at least 2 sides . \n");
	return -2;
	}
	if (dice < 1){
	printf ( "Need at least 1 die.\n") ;
	return -l;
	}
	for (d = 0; d < dice; d++)
		total += rollem(sides);
	return total;
}
//diceroll.h 包含该头文件的都可直接使用以下两个定义
extern int roll_count;//引用式声明
int roll_n_dice (int dice, int sides) ;

/*manydice.c --多次掷骰子的模拟程序 与diceroll.c一起编译*/
#include <stdio.h>
#include <stdlib.h>/*为库函数srand ()提供原型*/
#include <time.h>/*为time ()提供原型*/
#include "diceroll.h"/*为roll_n_dice()提供原型,为roll_count 变量提供声明*/
int main(void)
{
int dice, roll;
int sides;//面数
int status;//次数
srand ((unsigned int) time(0));/*随机种子*/
printf ("Enter the number of sides per die,0 to stop.\n");
while (scanf ( "%d", &sides) == 1 && sides > 0)
{//输入次数类型不匹配 面数<1  遇到文件结尾
	printf ( "How many dice ? \n");
	if((status = scanf("%d", &dice)) != 1)
	{
		if(status == EOF)
			break;/*退出循环*/
		else
		{
			printf ( "You should have entered an integer. " );
			printf ( " Let's begin again. \n" );
			while (getchar()!='\n')
				continue;/*处理错误的输入*/
			printf ("How many sides ? Enter 0 to stop. ln");
			continue;/*进入循环的下一轮迭代*/
		}
	}
	roll = roll_n_dice(dice,sides) ;
	printf ("You have rolled a %d using %d %d-sided dice. \n",roll,dice,sides);
	printf ("How many sides ? Enter 0 to stop.\n");
}
	printf ( "The rollem () function was called %d times. \n",roll_count) ;/*使用外部变量*/
	printf ( "GOOD FORTUNE TO YoU ! n");
	return 0;
}

动态分配内存

创建数组
1、数组名[常量表达式],静态内存或自动内存
2、变长数组,数组名[变量表达式],自动内存
3、指针=malloc()/calloc(),指针访问,静态或自动
后两种为动态数组,可在运行时选择数组的大小和分配内存

malloc free

静态内存编译时固定数量,自动内存执行时自动增加或减少
动态分配的内存数量只会增加,必须使用free释放,否则内存泄漏:指向内存的指针自动释放之后,内存无法被重复使用

malloc创建的数组可在其他函数通过指针访问
eg:被调函数创建数组、返回指针、主调函数在末尾free释放内存

double item [n] ; /* c99之前:n不允许是变量*/

/* dyn_arr.c --动态分配数组*/
#include <stdio.h>
#include <stdlib.h> /* 为malloc()、 free ()、EXIT_FAILURE提供原型*/
int main (void)
{
	double * ptd;
	int max;
	int number;
	int i = 0;
	puts ( "what is the maximum number of type double entries?");
	if (scanf ("%d",&max) != 1)
	{//获取数组大小
		puts ("Number not correctly entered -- bye." );
		exit (EXIT_FAILURE);/*EXIT_FAILURE表示程序异常中止
		EXIT_SUCCESS(相当于0)表示普通程序结束*/
	}
	ptd =(double *) malloc (max * sizeof (double));
	/*分配内存空间、赋值指针
	* 参数:需要的内存字节数,eg:存放30个double的数组大小
	* malloc找到合适的空闲内存块、分配内存但不赋名
	* 返回内存块的首字节地址、地址赋值给指针变量、使用指针访问这块内存
	* ANSI C之前返回 指向char的指针 
	* ANSI C之后返回 指向void的指针、强制转换为匹配的类型、eg :(double *) 返回指向double的指针赋值给ptd
	* ptd指向块的首元素,相当于数组名,ptd[0]
	* 分配失败返回NULL*/
	if (ptd == NULL)
	{//分配失败
		puts ( "Memory allocation failed. Goodbye . " ) ;
		exit(EXIT_FAILURE);
	}
	/*ptd 现在指向有max个元素的数组*/
	puts ( "Enter the values (q to quit): ");
	while (i < max & & scanf ("%lf", &ptd [i])== 1)
		++i;
	printf ( "Here are your %d entries : \n",number = i) ;
	for (i = 0; i < number; i++)
	{
		printf ("%7.2f ", ptd[i] );
		if (i % 7 == 6)
			putchar ( '\n' );
	}
	if (i %7 != 0)
		putchar ('\n');
	puts ("Done . ");
	free (ptd);
	/*只释放参数指向的内存块,参数:指向malloc分配的内存的指针
	内存存储期:malloc分配-free释放,malloc和free必须成对出现
	两个指针变量可以不相同,指向的存储地址必须相同*/
	long * newmem;
	newmem = (long *)calloc(100,sizeof(long));
	/*ANSI 之前返回char,之后返回void
	* 参数:存储单元数量,存储单元大小(字节),100个4字节的存储单元
	* 默认设置为0,和free成对出现*/
}

变长数组

自动存储类型,离开定义所在的块时,自动释放空间,无法在其他块使用

//动态二维数组
int n= 5;int m= 6;
int ar2[n][m];//n×m的变长数组(VLA)

int (* p2)[6];//c99之前的写法,指向内含6个int类型值的数组
p2 = (int (*)[6]) malloc(n * 6* sizeof(int)); //n×6数组

int (* p3)[m] ;//C99之后才可使用,支持变长数组,一个指向变长数组的指针
p3 = (int (*)[m]) malloc(n * m * sizeof(int)); //n×m数组(支持变长数组)
ar2[1][21= p2[1][2] = 12;

存储类别

3部分内存:
1、静态变量(外部链接、内部链接和无链接的):编译时确定,程序开始被创建,结束时被销毁
2、自动变量:进入定义所在块时创建、离开消失,随函数调用自动增加和减少,
3、动态内存分配:随malloc存在,随free释放,未使用的内存块分散在已使用的内存块之间,

//where.c --数据被储存在何处?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int static_store = 30;
const char * pcg = "string Literal";
int main()
{
	int auto_store = 40;
	char auto_string []= "Auto char Array" ;
	int * pi;
	char * pcl;
	pi = (int * ) malloc (sizeof (int) ) ;
	*pi = 35;
	pcl = (char *) malloc(strlen( "Dynamic string") + 1);
	strcpy(pcl,"Dynamic string" ) ;
	printf ("static_store: %d at %p\n",static_store,&static_store);
	printf ("auto_store: %d at %p\n",auto_store,&auto_store);
	printf("*pi : %d at %p\n",*pi, pi) ;
	printf("%s at %p\n", peg,pcg);
	printf("%s at %p\n", auto_string, auto_string) ;
	printf("%s at %p\n", pcl,pcl) ;
	printf("%s at %p\n","Quoted string" ,"Quoted string" );
	free(pi) ;
	free(pcl) ;
	return 0;
}

ANSI C类型限定符

描述变量 :类型+存储类别
C90新增两个属性
恒常性(constancy):const
易变性( volatility):volatile,
const和volatile创建的类型是限定类型(qualified type)

C99新增: restrict,用于提高编译器优化

C11新增:_Atomic(可选支持项),提供一个可选库,由 stdatomic.h管理,以支持并发程序设计

C99新增:幂等(idempotent):可以在一条声明中多次使用同一个限定符,多余的限定符将被忽略

const const const int n = 6;//与const int n = 6;相同
typedef const int zip;
const zip q =8;

const

初始化之后不可修改

类型 const *,指针不能用于改变指向的值
类型 * const,指针本身不能改变

声明为函数形参的指针,保证在函数中不会对传参数据进行更改
const声明全局变量/数组/结构,全局且不会被更改
1、在一个文件定义式声明,其他文件引用式声明,extern
2、头文件中定义static,包含头文件

const int nochange = 12;/*初始化时赋值没问题*/

/*const int nochange;限定nochange的值不能被修改
nochange = 12; error 初始化之后不可修改*/

const int days1[12] = { 31,28,31,30,31,30,31,31,30,31,30,31};//不允许修改的数组

const float * pf; 
/* pf指向一个float类型的const值
const float指向的值不可改变,可指向其他const值*/
float const * pfc; // 与const float * pfc;相同

float * const pt; 
/* pt是一个const指针
const pt 指针必须指向同一个地址,float指向的值可以改变*/

const float * const ptr;
/*const ptr不能指向别处,const float 指向的值也不可改变*/

void display(const int array[], int limit);//不能更改arry指向的数据

char *strcat (char * restrict s1,const char  restrict s2);//修改第1个字符串,不修改第2个字符串


/*file1.c --定义了一些外部const变量*/
const double PI = 3.14159;
const char * MONTHS[12] = { "January","February","March","April","May",
"June","July", "August","September","October""November", "December" };

/* file2.c --使用定义在别处的外部const变量*/
extern const double PI;
extern const * MONTHS [];


/*constant.h --定义了一些外部const变量*/
static const double PI =3.14159;
/*在头文件中声明时需要static ,否则在其他文件包含此头文件时会重复定义PI
包含此头文件,相当于给文件提供了单独的PI副本,只对该文件可见,数据是重复的
无法用这些数据和其他文件通讯*/
static const char * MONTHS[12] ={ "January","February","March","April","May",
"June" , "July", "August","September","October","November", "December" };
/*file1.c --使用定义在别处的外部const变量*/
#include "constant.h"
/*file2.c --使用定义在别处的外部const变量*/
#include "constant.h"

volatile

告知计算机:代理可以改变变量值
用于硬件地址、其他程序或同时运行的线程中共享数据

volatile int locl ; /*volatile变量、loc1是一个易变的位置*/
volatile int * ploc;/*volatile指针、ploc是一个指向易变的位置的指针*/

val1 = x;
/*一些不使用×的代码*/
val2 =x;
/*高速缓存:两次使用x之间没有修改x的值,x临时存储在寄存器中,从寄存器中读x
如果没有volatile,编译器无法确定使用X之前是否被修改,为安全不会使用高速缓存
volatile 关键字的存在让编译器确定:没有被volatile修饰的变量可以进行高速缓存
*/


volatile const int loc;
const volatile int * ploc;
/*硬件时钟,程序无法修改,代理可改变,限定符顺序不重要*/

restrict

修饰指针,表明该指针是访问数据对象的唯一且初始的方式,允许编译器优化代码

用于函数形参的指针:编译器假定函数内不会有标识符指向\修改该指针指向的数据,尝试对其优化

告知编译器:假定优化方案
告知用户:使用满足restrict要求的参数

int ar[10];
int *restrict restar=(int *)malloc(10 *sizeof(int));
/*指针restar是访问动态分配内存的唯一且初始的方式,设置为restrict */
int *par = ar;

for (n = 0; n< 10; n++)
{
	par[n] += 5;//1
	restar[n] += 5;//2
	ar[n] *=2;//3
	par[n] += 3;//4
	restar[n] += 3;//5
}
/*被restrict 修饰的restar,编译器可以合并语句2、5为restar[n] += 8;
未被restrict 修饰的par的语句1、4不可以合并为par[n]+=8;
因为par和ar指向同一位置,par不唯一。
使用restrict 可以让编译器选择捷径优化计算
*/

/*拷贝s2 n字节到s1*/
void * memcpy(void * restrict s1,const void * restrict s2,size_t n);
/*s1和s2都restrict ,说明指针是访问数据的唯一方式,不能访问相同块的数据,无重叠*/
void * memmove (void * s1,const void * s2,size_t n);/*有重叠*/

_Atomic

多线程
C11 可选头文件 stdatomic.h 和 threads.h 提供可选管理方法
通过宏函数访问原子类型
一个线程对一个原子类型的对象执行原子操作时,其他线程不能访问该对象

Atomic int hogs;//hogs是一个原子类型的变量
atomic_store(&hogs,12);//stdatomic.h中的宏,在hogs中存储12是一个原子过程,其他线程不可访问

旧词新用

C99

void ofmouth(int * const a1, int * restrict a2, int n);
/*以前的风格、a1是一个指向int的const 指针
不能更改指针本身,可以更改指针指向的数据。
a2是一个restrict 指针*/
void ofmouth(int a1[const],int a2[restrict],int n);
//c99允许、在声明函数形参时,指针表示法和数组表示法都可以使用这两个限定符

double stick ( double ar[static 20]) ;
//static新用法:修饰形参,实参是一个指向数组首元素的指针,且该数组至少有20个元素
  • 42
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值