C语言相似点对比

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jet007_007/article/details/52262619

1、NUL和NULL

在说两者区别之前,先说说自己遇到的例子。

在刚开始编程的时候,由于对字符串指针的初始化并不是太清楚,在初始化字符串指针str的时候,只写了如下代码:

char *str;
进行如上初始化之后,就想对其进行使用,而忽略了这个指针并未取得确定地址,指向不可用的空间,就对其进行如下操作:

printf("%s",str);
在Code::Blocks中打印结果为null。(注意:虽然在Code::Blocks中编译通过,也输出打印结果,但是这实际上是错误的)


接下来就以上边例子为基础说一下NULL和NUL的区别:

1、NULL:定义为0或0L或(void *) 0,用于指示一个指针值是空,即什么都不指。

2、‘\0’:用且只用字符串结束符,为字符常量;NUL是字符串常量的名字。

3、NUL:0x00,0值字符,用于结束ASCII字符串,和'\0‘类似(可以理解为别名),但是在C/C++中没有定义,如果要使用的话,需要自定义为#define NUL '\0';。

EOF:通常定义为-1,文件结束符标志,一般用ctrl+z。

NULL是在<stddef.h>头文件中专门为空指针定义的一个宏。NUL是ASCII字符集中第一个字符的名称,它对应于一个零值。

NULL可以被定义为(void *) 0,而NUL可以被定义为‘\0’。NULL和NUL都可以被简单地定义为0,这时它们是等价的,可以互换使用,但这是一种不可取的方式。为了使程序读起来更清晰,维护起来更容易,你在程序中应明确地将NULL定义为指针类型,而将NUL定义为字符类型。

对指针进行解引用操作可以获得它的值。从定义来看,NULL指针并未指向任何东西。因此,对一个NULL指针进行解引用操作是非法的。在对指针进行解引用操作之前,必须保证它并非NULL指针。

因此,在进行如下操作时,程序会自动终止。

char *str;
*str="123";
也就是说,第一个语句将str声明为一个指向NULL的指针(此说法不准确,只是说明并未取得确定地址),第二条语句在对其进行解引用时引起程序终止。

注意,可以写成如下形式,在下面形式中str指向字符串常量所存储空间的首地址。

char *str;
str="This is a C program!";


下面这条语句将str初始化为一个指向字符串常量的指针。

char *str="123";


‘\0’都位于哪些位置?

1、指定大小的字符串数组
const char m1[40]="Limit yourself to one line's worth."
指定数组大小时,一定要确保数组元素数比字符串长度至少多1(多出来的1个元素用于容纳空字符)。 未被使用的元素均被自动初始化为‘\0’。
2、让编译器决定数组大小
const char m1[]="Limit yourself to one line's worth."
每个元素对应一个字符,一个附加的元素对应结束的空字符‘\0’。
3、字符串指针
也是在最后自动附加一个空字符‘\0’。


2、声明和定义的区别

以多文件中的全局变量定义和声明为例来引入声明和定义的区别。

项目文件夹project下有main.c、common.c和common.h三个文件,其中common.h文件分别#include在main.c和common.c文件中,现在希望声明一个字符型变量key,在main.c和common.c中公用。

正确的解决办法:在其中一个.c文件中定义一个全局变量key,然后在另一个要使用key这个变量的.c文件中使用extern关键字声明一次,说明这个变量为外部变量,是在其他的.c文件中定义的全局变量。例如,在main.c文件中定义变量key,在common.c文件中声明key变量为外部变量,这样两个文件中就能共享这个变量key了。

例如:

main.c文件:

#include "common.h"
unsigned char key;
common.c文件

#include "common.h"
extern unsigned char key;
其实这就是 变量定义变量声明的区别,变量定义使用“数据类型+变量名称”的形式,编译器需要给他分配内存单元的;而变量声明使用“extern 变量类型+变量名称”的形式,是告诉编译器我这个变量将在其他外部.c文件中定义,我在这里只是在外部用它,编译器就不给他分配内存空间,而等到真正遇到变量定义的时候再给他分配内存空间。


变量定义:所谓的定义就是(编译器)创建一个对象,为这个对象分配一块内存并给他取上一个名字,这个名字就是我们经常所说的变量名或对象名。一个变量或对象在一定的区域内(比如函数内、全局等)只能被定义一次;如果定义多次,编译器就会提示用户重复定义了同一个变量或对象。


变量声明:有两重含义

1、告诉编译器,这个名字已经匹配到一块内存上了,下面的代码用到变量或对象是在别的地方定义的。声明可以出现多次。

2、告诉编译器,这个名字已被预定了,别的地方再也不能用它来作为变量名或对象名。

3、字符串指针和字符数组

在说两者区别之前,还来说一下自己在不熟悉的时候碰到的例子。

在实际应用中,我想应用一片字符串空间,也就是向一片地址空间中写入一个字符串,只定义了字符串指针变量char * str;,便调用myitoa()函数,将str作为要写入转换结果的目标字符串的时间参数。

char *str;


/***************************将整型数转换为字符串形式*********************************/
/**************************************************************************************
    参数num:要转换的数字
    参数str:要写入转换结果的目标字符串
    参数radix:转换数字时使用的基数
**************************************************************************************/
char *myitoa(int num,char *str,int radix)
结果程序自动终止。

先说一下出现上面这种情况的原因。

在char *str;中,字符串指针变量未取得确定地址,也就是str指向的地址空间不可用(可能存有重要数据),在这种情况下往内存中写入数据是错误的。


字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以‘\0’作为串的结束。字符数组是由若干个数组元素组成的,它可用来存放整个字符串。


1、未取得确定地址的字符串指针

char *str;
在这种形式下,字符串指针变量str指向不可用的地址空间,不能对其进行相应的写入操作。
在定义指针变量时,编译器并不为指针所指向的对象分配空间,它只分配指针变量本身的空间,除非在定义的像下边2中那样同时赋值给指针变量一个字符串常量进行初始化。

2、字符串指针在定义中用字符串常量进行初始化

char *str;
str="This is a C program!";

char *str="This is a C program!";
在这种形式下,像上边1中所述的那样,在ANSI C中,初始化指针所创建的字符串常量被定义为只读,如果试图通过指针修改这个字符串的值,程序就会出现未定义的行为。在有些编译器中,字符串常量被存放在只允许读取的文本段中,以防止它被修改。

回到刚开始时所介绍的自己碰到的情况,在刚开始的时候,我知道只定义指针变量指向的空间不可用,所以为了使指针指向可用的空间,我将其初始化为char *str="\0",在调用myitoa()函数,结果程序仍然自动终止。在这里,指向字符串常量的指针指向的空间是不可修改的,但是指针变量是可以改变的,也就是可以将一个字符数组char str1[]中的str1复制给指针变量str。吐舌头吐舌头吐舌头

字符数组在定义中用字符串常量进行初始化

char str[]="This is a C program!";

与指针相反,由字符串常量初始化的数组是可以修改的,其中的单个字符在以后可以改变。
仍然回到刚开始时所介绍的自己碰到的情况,后来,我定义了了一个指定大小的字符串数组,指定大小的字符串数组编译器给分配空间,此时再调用myitoa()函数,程序运行正常。吐舌头吐舌头吐舌头

在此,对刚开始碰到的情况作出总结,要使指针指向可用的地址空间,可以用数组的方法或者给字符指针分配内存空间的方法。用法如下:

char name[20];
scanf("%s",name);
printf("%s",name);

char *name;
name=(char *)malloc(20);
scanf("%s",name);
printf("%s",name);
参考:http://blog.csdn.net/okliujieko/article/details/6821321

3、

4、double和float

double:符号1位,指数11位,尾数52位;

float:符号1位,指数8位,尾数23位。

float和double的不同,除了范围,更重要的是精度。按10进制看,float大概在小数点后6~7位,double能达到15位。1.2345678912用double可以精确表示,用float要发生截断。

从存储结构和算法上来讲,double和float是一样的,不一样的地方仅仅是float是32位的,double是64位的,所以double能存储更高的精度。

5、break与continue的区别

break表示终止本层循环;
continue表示终止本次(本轮)循环。


6、float *g()和float (*h)()

在区分这两个概念之前,先加强一下什么是C变量的声明。任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符(declarator)。声明符从表面上看与表达式有些类似,对它求值应该返回一个声明中给定类型的结果。

1、声明单个变量:

float f,g; 含义:当对其求值时,表达式f,g的类型为浮点数类型

2、声明函数

float ff(); 含义:表达式ff()求值的结果是一个浮点数,也就是:ff是一个返回值为浮点类型的函数

3、声明指针

float *pf; 含义:*pf是一个浮点数,也就是:pf是一个指向浮点数的指针

从上面一些简单声明的例子可以看出,在分析声明的时候,都可以看作对声明符求值时返回类型为修饰符的类型,然后再进行进一步的分析

接下来就按照上面的步骤来分析float *g()和float (*h)()。

float *g(); 其实我感觉在这里可以把float *看作一个类型修饰符,按照上面的分析步骤也就是g()返回一个指向float类型数的指针,但是g()本身又是一个函数,在此就可以理解为g是一个函数,返回值类型为指向浮点数的指针。

float (*h)(); 按照上边的分析步骤,对其求值时,表达式(*h)()的类型为浮点数类型,然后进一步分析,(*h)()本身又是一个函数,h则是一个函数指针,h所指向函数的返回值为浮点类型。h是一个返回值为浮点类型的函数(例如:float ff(),ff就为一个返回值为浮点类型的函数)的指针。






猜你喜欢

转载自blog.csdn.net/jet007_007/article/details/52262619