C语言Word文档格式.docx

上传人:b****5 文档编号:17978882 上传时间:2022-12-12 格式:DOCX 页数:18 大小:28.29KB
下载 相关 举报
C语言Word文档格式.docx_第1页
第1页 / 共18页
C语言Word文档格式.docx_第2页
第2页 / 共18页
C语言Word文档格式.docx_第3页
第3页 / 共18页
C语言Word文档格式.docx_第4页
第4页 / 共18页
C语言Word文档格式.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

C语言Word文档格式.docx

《C语言Word文档格式.docx》由会员分享,可在线阅读,更多相关《C语言Word文档格式.docx(18页珍藏版)》请在冰豆网上搜索。

C语言Word文档格式.docx

从以上分析可以看出,把局部变量改变为静态变量后是改变了他的存储方式,即改变了他的生存期。

把全局变量改变为静态变量后是改变了他的作用域,限制了他的使用范围,因此static这个说明符在不同的地方起的作用是不同的。

TIPS:

1、若全局变量仅在单个文件中访问,则可以讲这个变量修改为静态全局变量。

2、若全局变量仅在单个函数中使用,则可以将这个变量修改为该函数的静态局部变量。

3、全局变量、静态局部变量、静态全局变量都存放在静态数据存储区。

4、函数中必须要使用static变量的情况:

当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。

Stack/heap/static(存储区)

一、预备知识—程序的内存分配 

 

一个由C/C++编译的程序占用的内存分为以下几个部分 

1、栈区(stack)— 

由编译器自动分配释放 

,存放函数的参数值,局部变量的值等。

其 

操作方式类似于数据结构中的栈。

2、堆区(heap) 

— 

一般由程序员分配释放, 

若程序员不释放,程序结束时可能由OS回 

收 

注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的 

全局变量和静态变量在一块区域, 

未初始化的全局变量和未初始化的静态变量在相邻的另 

一块区域。

程序结束后由系统释放。

4、文字常量区 

—常量字符串就是放在这里的。

程序结束后由系统释放 

5、程序代码区—存放函数体的二进制代码。

二、例子程序 

这是一个前辈写的,非常详细 

//main.cpp 

int 

0;

全局初始化区 

char 

*p1;

全局未初始化区 

main() 

b;

栈 

s[] 

"

abc"

;

*p2;

*p3 

123456"

123456/0在常量区,p3在栈上。

static 

=0;

全局(静态)初始化区 

p1 

(char 

*)malloc(10);

p2 

*)malloc(20);

分配得来得10和20字节的区域就在堆区。

strcpy(p1, 

);

123456/0放在常量区,编译器可能会将它与p3所指向的"

优化成一个地方。

内存释放之后

既然使用free函数之后指针变量p本身保存的地址并没有改变,那我们就需要重新把p的值变为NULL:

?

1

p=NULL;

这个NULL就是我们前面所说的“栓野狗的链子”。

如果你不栓起来迟早会出问题的。

比如:

在free(p)之后,你用if(NULL!

=p)这样的校验语句还能起作用吗?

例如:

2

3

4

5

6

7

8

9

char*p=(char*)malloc(100);

strcpy(p,“hello”);

free(p);

/*p所指的内存被释放,但是p所指的地址仍然不变*/

if(NULL!

=p)

{

/*没有起到防错作用*/

strcpy(p,“world”);

/*出错*/

}

释放完块内存之后,没有把指针置NULL,这个指针就成为了“野指针”,也有书叫“悬垂指针”。

这是很危险的,而且也是经常出错的地方。

所以一定要记住一条:

free完之后,一定要给指针置NULL。

realloc申请内存

1.分配内存空间函数malloc

  调用形式:

(类型说明符*)malloc(size)功能:

在内存的动态存储区中分配一块长度为"

size"

字节的连续区域。

函数的返回值为该区域的首地址。

“类型说明符”表示把该区域用于何种数据类型。

(类型说明符*)表示把返回值强制转换为该类型指针。

“size”是一个无符号数。

pc=(char*)malloc(100);

表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc。

2.分配内存空间函数calloc

  calloc也用于分配内存空间。

调用形式:

(类型说明符*)calloc(n,size)功能:

在内存动态存储区中分配n块长度为“size”字节的连续区域。

(类型说明符*)用于强制类型转换。

calloc函数与malloc函数的区别仅在于一次可以分配n块区域。

ps=(struetstu*)calloc(2,sizeof(structstu));

其中的sizeof(structstu)是求stu的结构长度。

因此该语句的意思是:

按stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps。

realloc(void*__ptr,size_t__size):

更改已经配置的内存空间,即更改由malloc()函数分配的内存空间的大小。

如果将分配的内存减少,realloc仅仅是改变索引的信息。

如果是将分配的内存扩大,则有以下情况:

1)如果当前内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()将返回原指针。

2)如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。

3)如果申请失败,将返回NULL,此时,原来的指针仍然有效

Free释放内存

5.内存已经被释放了,但是继续通过指针来使用

只是释放了内存的使用权,内存仍可通过指针来调用

这里一般有三种情况:

第一种:

就是上面所说的,free(p)之后,继续通过p指针来访问内存。

解决的办法就是给p置NULL。

第二种:

函数返回栈内存。

这是初学者最容易犯的错误。

比如在函数内部定义了一个数组,却用return语句返回指向该数组的指针。

解决的办法就是弄明白栈上变量的生命周期。

链表

#include<

stdio.h>

string.h>

stdlib.h>

typedefstructDNode

{

inti;

intdata;

structDNode*prior;

structDNode*next;

}DNode,*DoubleNode;

structDNode*Listinit(structDNode*top)

top=(structDNode*)malloc(sizeof(DNode));

top->

prior=NULL;

next=NULL;

i=1;

printf("

%d$\n"

top->

i);

returntop;

voidListadd(structDNode*head)

structDNode*top;

intAdd_Data;

while(head->

next!

=NULL)

head=head->

next;

}

pleaseinputdata_num:

"

scanf("

%d"

&

Add_Data);

head->

next=top;

prior=head;

data=Add_Data;

data=%d\n"

data);

%d:

#\n"

head->

i=head->

i+1;

\n"

voidListdel(structDNode*head)

intnum;

璇疯緭鍏ラ渶瑕佸垹闄よ妭鐐圭殑缂栧彿:

num);

i!

=num&

&

head=head->

if(head->

next==NULL&

=num)

{

exit(0);

prior->

next=head->

next->

prior=head->

prior;

free(head);

voidrelease(structDNode*head)

:

structDNode*top;

while(head)

top=head;

%d&

head=head->

free(top);

intmain()

structDNode*head=Listinit(NULL);

Listadd(head);

Listdel(head);

release(head);

C语言优先级

一共有十五个优先级:

从上到下降低

() 

[] 

->

!

 

-(负号)++ 

-- 

&

(取变量地址)* 

(type)(强制类型) 

sizeof 

*/%

+- 

>

>

<

=<

==!

10 

11 

12 

||

13 

14 

+= 

-= 

*= 

/= 

%= 

|= 

^= 

<

=

15 

就着多吧 

结合性:

13 

是从右至左 

其他都是 

从左至右有问题可以在交流的 

待续

exit()是一个函数

结束一个进程,它将删除进程使用的内存空间,同时把错误信息返回父进程,在父进程中wait系统调用将接受到此返回信息。

return返回函数值,是关键字,返回后改程序结束;

在main函数中我们通常使用return(0);

这样的方式返回一个值。

但这是限定在非void情况下的也就是voidmain()这样的形式。

exit()通常是用在子程序中用来终结程序用的,使用后程序自动结束跳回操作系统。

但在如果把exit用在main内的时候无论main是否定义成void返回的值都是有效的,并且exit不需要考虑类型,exit

(1)等价于return

(1)

exit(0);

//正常退出

非0即是非正常退出

数字0,1,-1会被写入环境变量ERRORLEVEL,其它程序可以由此判断程序结束状态。

一般0为正常推出,其它数字为异常,其对应的错误可以自己指定。

#ifndef与#endif

这个是C语言或C++语言条件编译的表示方法。

并不一定用于头文件。

其形式为

#ifndefMACRO_NAME

codes;

#endif

其含义为,当MACRO_NAME这个宏没有被定义的时候,codes部分的代码才会被编译,否则codes部分将被忽略。

为了避免头文件被重复引用,在头文件中一般会加入类似于

#ifndefXXXX

#defineXXXX

codes

这样的代码。

其中XXXX这个宏名由头文件名衍生而来。

如a.h,XXXX可以定义为_A_H_。

当头文件被第一次引用时,XXXX未定义,codes部分被编译,同时定义宏XXXX。

当同一源文件第二次引用该头文件时,XXXX已经被定义了,codes部分不会被二次编译,从而避免重复引用。

条件编译

预处理程序提供了条件编译的功能。

可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。

这对于程序的移植和调试是很有用的。

条件编译有三种形式,下面分别介绍:

1. 

第一种形式:

#ifdef 

标识符

程序段1

#else

程序段2

#endif

它的功能是,如果标识符已被 

#define命令定义过则对程序段1进行编译;

否则对程序段2进行编译。

如果没有程序段2(它为空)

2. 

第二种形式:

#ifndef 

与第一种形式的区别是将“ifdef”改为“ifndef”。

它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译, 

这与第一种形式的功能正相反。

3. 

第三种形式:

#if 

常量表达式

它的功能是,如常量表达式的值为真(非0),则对程序段1 

进行编译,否则对程序段2进行编译。

因此可以使程序在不同条件下,完成不同的功能

pthread_mutex_t

1.互斥锁创建

有两种方法创建互斥锁,静态方式和动态方式。

POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:

pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;

在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

  动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:

intpthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutexattr_t*mutexattr)

其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

  pthread_mutex_destroy()用于注销一个互斥锁,API定义如下:

intpthread_mutex_destroy(pthread_mutex_t*mutex)

销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。

由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

  2.互斥锁属性

  互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。

当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

  *PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。

当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。

这种锁策略保证了资源分配的公平性。

  *PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。

如果是不同线程请求,则在加锁线程解锁时重新竞争。

  *PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。

这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。

  *PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

  3.锁操作

  锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。

对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;

而检错锁则必须由加锁者解锁才有效,否则返回EPERM;

对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。

在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

  intpthread_mutex_lock(pthread_mutex_t*mutex)

  intpthread_mutex_unlock(pthread_mutex_t*mutex)

  intpthread_mutex_trylock(pthread_mutex_t*mutex)

  pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

#define

#include<

#define 

A(x) 

x+x

B(x) 

(x+x)

intmain()

intx=5;

%d\n"

A(x)*A(x));

B(x)*B(x));

return0;

运行下你就明白了。

因为类函数宏只是简单的替代而不是函数:

A(x)*A(x)=x+x*x+x=35;

B(x)*B(x)=(x+x)*(x+x)=100;

typedef常见用法

1.常规变量类型定义

typedefunsignedcharuchar

描述:

uchar等价于unsignedchar类型定义ucharc声明等于unsignedcharc声明

2.数组类型定义

typedefintarray[2];

array等价于int[2]定义;

arraya声明等价于inta[2]声明

扩展:

typedefintarray[M][N];

array等价于int[M][N]定义;

arraya声明等价于inta[M][N]声明

3.指针类型定义

typedefint*pointer;

pointer等价于int*定义;

pointerp声明等价于int*a声明

typedefint*pointer[M];

pointer等价于int*[M]定义pointerp声明等价于int*a[M]声明明

4.函数地址说明

C把函数名字当做函数的首地址来对待,我们可以使用最简单的方法得到函数地址

函数:

intfunc(void);

unsignedlongfuncAddr=(unsignedlong)func,funcAddr的值是func函数的首地址

5.函数声明

typedefintfunc(void);

func等价于int(void)类型函数

描述1:

funcf声明等价于intf(void)声明,用于文件的函数声明

描述2:

func*pf声明等价于int(*pf)(void)声明,用于函数指针的生命,见下一条

6.函数指针

typedefint(*func)(void)

func等价于int(*)(void)类型

funcpf等价于int(*pf)(void)声明,pf是一个函数指针变量

7.识别typedef的方法:

a).第一步。

使用已知的类型定义替代typdef后面的名称,直到只剩下一个名字不识别为正确

如typedefu32(*fun

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 数学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1