C语言学习记录


//做个实验来看看c是怎样使用内存的?

//做的实验来看看c是怎样使用内存的?
#include<stdio.h>
#include<stdlib.h>
 
int global_variable;
static int file_static_variable;
void func1(void )
{
                 int func1_variable;
                 static int func1_static_variable;
                printf( "&func1_variable..%p\n" ,&func1_variable);
                printf( "&func1_static_variable..%p\n" ,&func1_static_variable);
 
}
void func2(void )
{
                 int func2_variable;
                printf( "&func2_variable..%p\n" ,&func2_variable);
}
int main()
{
                 void *p;
 
                 /*输出指向函数的指针*/
                printf( "&func1..%p\n",func1);
       
                printf( "&func2..%p\n",func2);
                 /*输出字符串常量的地址*/
                printf( "string literal..%p\n","abc" );
                 /*输出全局变量*/
                printf( "&global_vaiable..%p\n" ,&global_variable);
                 /*输出文件中的static变量的地址*/
                printf( "&file_static_variable..%p\n" ,&file_static_variable);
                 /*输出局部变量*/
                func1();
                func2();
                 /*通过malloc申请的内存区域的地址*/
                p=malloc( sizeof(int ));
                printf( "malloc address..%p\n",p);
 
                 return 0;
}
 
 
 
地址一览表
 
 
 


地址                                    内容                                          地址                                          内容


00391208                            函数func1()的地址                   00398480                                 全局变量
00391203                            函数func2()的地址                   00452D20                    利用malloc()分配的内存区域
00395864                            字符串常量的地址                    0024FC88                          func1()中自动变量
00398488                            函数内static变量                       0024FC88                         func2()中自动变量
00398484                            文件中static变量


通过观察发现,指向函数的指针和字符串常量被配置在非常近的内存区域,此外,函数内static变量,文件内static
变量,全局变量等这些静态变量,也是在非常近的
内存区域。接下来是malloc()分配的内存区域,它看上去和自动变量的区域离得很远。最后你可以发现,func1()和func2()的自动变量被分配了完全相同的内存地址。
 
 
 
 
 

指针笔记

c语言为什么不做数组下标越界检查?

通常,c对数组的长度范围是不做检查的。因此,当向数组写入数据的时候,经常产生“内存被破坏”的问题,在较早的阶段,操作系统发现异常并且提示segmentation fault "或者强制关闭异常的应用程序"这样的消息还算幸运,最不幸的是,相邻变量的值已经被破坏,程序却还在继续运行,并且你无法预知悲剧会在何时何地发生。
可以使用int a[10];这样的方式声明数组,并且通过a[i]的方式引用数组元素的那些编程语言,可以比较容易的进行数组长度范围检查,但是对于c,当数组出现在表达式中的时候,它会立刻被解读成指针,此外,使用其他的指针变量也可以指向数组的任意元素,并且这个指针可以任意进行加减运算。
引用数组元素的时候,虽然你可以写成a[i],但它不过是*(a+i)的语法糖。
还有,当你向一个函数传递数组的时候,实际上你传递的是一个指向初始元素的指针。如果这个函数还存在与其他的代码文件中(另外一个编程单元),那么通过编译器是不可能追踪到数组的。
如果无论如何都需要进行数组长度检查,可以考虑将指针封装成结构体那样,运行时让指针自身持有可取值范围的信息,可是这么做对性能的影响很大,同时,也丧失了非调试模式下编译后的库和指针的兼容性。
总的来说,除了某些解释型的编程语言之外,目前几乎没有编译器可以为我们做数组的越界检查。

存储类型修饰符

在c的语法中,以下关键字被定义位“存储类型修饰符”。
typedef     extern    static         auto   register
可是这些关键字中,真正是“指定存储区间”的关键字,只有static。
extern使得在其他地方定义的外部变量可以在本地可见;auto是默认的,所以没有显式指定的必要;register可以给出编译器优化提示(如今的编译器已经很先进了,所以一般不会使用这个关键字);至于typedef,它只是因为可以给编码带来便利才被归纳到存储类型修饰符中来的。
 

 

函数传值

在c中是不能将数组作为函数参数进行传递的,但是,你可以通过传递指向初始元素的指针,来达到将数组作为参数进行传递的目的。
要点:
如果试图将数组作为函数参数进行传递,那就传递指向数组的初始元素的指针。
在c中,函数参数传递都是传值,向函数传递的都是参数的副本。当前的例子同样如此,向get_word()传递的是指向buf初始元素的指针的副本,但是,main()和get_word()引用的都是buf本身,而不是buf的副本,正因为如此,get_word()才能正确的向buf填充字符串的内容。
在迫不得已的情况下,如果执意要将数组的副本作为参数进行传递,可以使用替代方法——将数组的所有元素组成结构体的成员。
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
 
int get_word(char *buf,int buf_size, FILE *fp )
{
                 int len;
                 int ch;
                 while ((ch=getc(fp ))!=EOF&&!isalnum(ch))
 
                                ;
                 if(ch==EOF )
                                 return EOF ;
                len=0;
                 do{
                                 buf[len]=ch;
                                len++;
                                 if(len>=buf_size ){
                                                fprintf( stderr,"word too long.\n" );
                                                exit(1);
                                                                                }
                } while((ch=getc(fp ))!=EOF&&isalnum(ch));
                 buf[len]='\0' ;
                 return len;
}
int main(void )
{
                 char buf[256];
                 while (get_word(buf,256, stdin )!=EOF){
                                printf( "<<%s>>\n",buf);
 
                }
}
}

修改参数,好吗?

 
对于函数中的传值参数尽量不要修改它的值,函数的参数是从调用方得到的非常重要的信息,如果一时疏忽的修改了参数,就再也恢复不了了,对于在后面追加新的逻辑,或者调试程序的情况下,因为原始的参数已经被修改,如果想看一下参数的的值,你会感觉非常棘手。
此外参数应该有一个有意义的名称。在修改参数的时候,违背最初参数名称的意义的“恶行”也屡见不鲜。

空指针是一个特殊的指针 。

 
 
空指针是一个特殊的指针 。
空指针是指可以确保没有指向任何一个对象的指针。通常使用宏定义NULL来表示空指针常量值。空指针确保它和任何非空指针进行比较都不会相等,因此经常作为函数发生异常时的返回值使用。

下标运算符[ ]和数组是没有关系的

下标运算符[ ]和数组是没有关系的
像下面这样将指针指向数组的初始元素
p=&array[0];
其实也可以写成下面这样:
p=array;
不能说
在c中,如果数组名后不加[ ],单独的止血数组名,那么此名称就表示“指针数组初始元素的指针”。
下面依法证明其错误的原因。
将&array[0]改写成array,“改写版”的程序甚至可以写成下面这样:
p=array;
for(i=0;i<5;i++)
{
    printf("%d\n",*(p+i));
 
}
另外,程序中*(p+i)还可以写成p[i]。
p=array;
for(i=0;i<5;i++){
  printf("%d\n",p[i]);
}
也就是说
*(p+i)和p[i]是同样的意思,
然而p[i]又与array[i]没有区别,所以array[i]可以和p[i]一样,将arry解读成“指向数组的初始元素的指针”。
也就是说,存在
int array[5];
这样的声明的时候,“一旦后面不加[ ],只写array”并不代表要是array具有指向数组的初始元素的指针的含义,无论加不加[ ],在表达式中,数组都可以被解读成指针。
要点
p[i]是*(p+i)的简便写法。
下标运算符[ ]原本只有这种用法,它和数组无关。
需要强调的是,认为[ ]和数组梅雨关系,这里的[]是指在表达式中出现的下标运算符[ ].
声明中的[],还是表达数组的意思,也就是说声明中的[]和表达式中的[]意义完全不同。表达式中的*和声明中的*也是完全不同的。
引用数组的时候,通常可以正确的使用array[5]这样的写法,其实,就算你写成5[array],还是可以正确引用到你想要的元素。
要点
p[i]可以写成i[p].
不过不要写成这样。

 

C是本来只能使用标量(scalar)的语言

C是只能使用标量(scalar)的语言
标量就是指char,int ,double和枚举型等数值类型,以及指针。相对的,像数组,结构体 ,和共用体这样的将多个变量进行组合的类型,称之为聚合类型。
#include<stdio.h>
#include<conio.h>
int main()
{               char str[10];
                gets(str);
                 //getch();
                 if(*str==*"abc" )      //不能写成if(str=="abc") 这里比较的是指针;因为字符串数组是聚合类型,在c里面不能用==比较
                                printf( "YES");
                 else printf("NO" );
                printf( "\n");
                puts(str);
 
 
                printf( "%p\n",str);   //%p能输出指针的值,输出的均为指针所对应的虚拟地址,而非物理地址
                 return 0;
}
posted @ 2015-01-14 10:14  下笔笔  阅读(273)  评论(0编辑  收藏  举报