`
maosuhan
  • 浏览: 110147 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

c语言归纳

    博客分类:
  • cc++
阅读更多

在c里面fscanf会遇到空格就停止。而且着里面如果你的参数是%s的话还需要注意,字符串读取会默认一直到这一行的末尾或者是空格而结束。着里面fscanf有几个高级的功能,类似于正则表达式的,比如

        1、 * 亦可用于格式中, (即 %*d 和 %*s) 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中)

  2、{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d。   

        3、width表示读取宽度。   

        4、{h | l | I64 | L}:参数的size,通常h表示单字节size,I表示2字节 size,L表示4字节size(double例外),l64表示8字节size。   

        5、type :这就很多了,就是%s,%d之类。

        6、%[a-z] 表示匹配a到z中任意字符,贪婪性(尽可能多的匹配)。%[aB'] 匹配a、B、'中一员,贪婪性   %[^a] 匹配非a的任意字符,贪婪性。这里的%应该是集合的意思吧!

 

对于像1,fasdfasdf;d,66,8 这样的应该这样读取

 fscanf(fp,"%d,%[^;];%c,%d,%d",&num,(char *)(&s),&c,&num2,&num3);

 

 

c字符串赋值 

  char s[]="dkasjsda";这个其实是在栈上声明空间,所以可以进行对数组的修改。而char *s2="dasdasasd";其实是声明了一个指针,其指向了静态存储区的一个字符串常量,不能改变其内容,否则会抛出segmentation fault的错误。

你也可以这样赋值  char s3[]={'d','f','d','d','e','e','r','\0'}。和char s[]="dkasjsda"效果是一样的。sizeof(s3)是8而sizeof(s2)是4。

s3的类型是char[8],s2的类型是char* 。

但你声明了一个数组时,你就不能在另外的语句里面再对他进行赋值了。比如char s[22];s="dasasd";是错的。只能一个元素一个元素的赋值,至于为什么会这样我也不知道。

 

 

const的用法

可以节省空间,避免不必要的内存分配。 例如: 

#define PI 3.14159 //常量宏 

const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ...... 

double i=Pi; //此时为Pi分配内存,以后不再分配! 

double I=PI; //编译期间进行宏替换,分配内存 

double j=Pi; //没有内存分配 

double J=PI; //再进行宏替换,又一次分配内存! 

const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是 象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。 

    编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也 很高。

修饰常指针 

const int *A; //const修饰指向的对象,A可变,A指向的对象不可变 

int const *A; //const修饰指向的对象,A可变,A指向的对象不可变 

int *const A; //const修饰指针A, A不可变,A指向的对象可变 

const int *const A;//指针A和A指向的对象都不可变

 

 

 

gcc编译器下:
c里面的变量分为三种,具有外部连接性质的,内部连接性质的还有不具有连接性质的。比如我在一个c文件a.c里面定义了一个const int a=9;那么我可以在另一个b.c文件中在函数外写int b;(也可以写const int b,也可以写extern int b)。这里的b的指仍然是9,奇怪为什么不一定要用extern呢?而你也不能在b.c里面改变它的值,会抛出段错误。如果在a.c中有一个static int b=7;那么在b.c中就再也引用不到了,因为static变量只具有内部连接性,只能在a.c中被访问。而你在a.c里面如果有一个int add(int a,int b){return a+b;}那么在b.c中竟然可以直接调用,也不需要extern。这就是连接。那我们要头文件有什么用呢?我们好像没有用到过头文件现在……

头文件作用:(1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。 (2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。
其实没有头文件我们照样可以写出程序,但是这里的头文件就像是暴露给用户看的借口或者说是文档一样,是一个辅助性的东西。但是又是编程的一部分。

在c语言里,对象必须有且只有一个定义,但是它可以有多个extern声明。定义与声明的区别,声明所说明的并非是对象自身,而是描述其他地方的创建的对象。定义相当于是特殊的声明,它为对象分配内存。

数组与指针,你看我定义一个数组时候,char s[100],这里其实并没有在空间里申请一个变量保存s的地址,然后s的内容是一个地址指向了字符序列。这里其实在编译之后s就没有了,字符序列的地址已经确定了。你s[2]的时候进行的只是地址的操作,而不是先取s的指向的地址,再偏移,再取内容,这样太麻烦。因为s和地址直接绑定了,直接偏移就可以了。而指针不一样,那是动态的,你用char *s="12312";s[2],其实是先取s的指向的地址,再偏移,再取内容。试想如果你在一个文件里char s[]="1231231",然后在另一个文件里extern char *s;s[2]会发生什么?因为s的地址(不是s存的地址,是s本身的地址)的是s所固定绑定的地址。但是它会把原来的顺序的asc码当作是地址再来进行偏移,这个显然是荒谬的。记住extern不申请额外的空间,这里都是extern在搞怪。但是如果你char s[]="12123";char *s2=s是可以的。

关于指针和数组我还要讲一些,数组名是不可以修改其值的,是不可修改的左值,因为它在编译时就确定了。而指针是可以改变指向的。作为函数参数的数组(在一个函数调用中)始终会被编译器修改为指向数组第一个元素的指针。没有办法把数组本身传递给函数,这个命题似乎有点怪。什么叫数组本身。
c语言中的多维数组其实是数组的数组。注意在数组给函数当参数的时候,形式参数总会是指针的形式。比如 char c[8][10]会改写成char*c[10]。指针数组char *c[15]会改成char**c。数组指针char(*c)[64]不变,char**c不变。在c语言中,没有办法向函数传递一个普通的多维数组。这是因为我们需要知道每一维的长度,以便为地址运算提供正确的单位长度。在c语言中,我们没有办法在实参和形参之间交流这种数据。因此你必须提供出了最左边一维以外的所有维的长度。这样就把实参限制在除左边一维外所有维都匹配的数组。


在c里面,指针赋值的时候有这么一个约定。两个操作数都是指向有限定符或是无限定符的相容类型的指针,左边的指针所指向的类型必须有右边指针所指向类型的全部限定符。
比如char * cp;const char * cpp;cpp=cp;这个就是对的。因为const char 和 char是相容的,本质都是char。而cpp包含了cp,所以可以。而cp=cpp就是不对的。
而如果char **a1;const char **a2;那么彼此则不能赋值。因为const char*和char *其实是不相容的,因为类型是不一样的,一个是指向const char 的指针一个是指向char 的指针。而如果是char **a1;char * const a2;就可以a2=a1;

const和*的组合通常只是用在数组形式的参数中模拟传值调用。
如果有一个语句是malloc(strlen(str))那么可以肯定那是错误的,而malloc(strlen(str)+1)才是对的。
一旦一个指针进行解除引用操作时所引用的内存地址超出了虚拟内存的地址空间,操作系统会中止这个进程。
break语句是跳出的最近的那层循环或是switch语句。
几乎c所有的函数都是默认用extern限定。但是这个其实是一个缺陷,软件对象在大多数情况下应该缺省采用有限可见性,如果要声明是全局的,应采用显示的声明。在c语言中,对信息的可见性的选择是很有限的。

[]的优先级要高于*,int *ap[]意思是ap是个指向int指针的数组。
函数()高于*,所以函数指针得这么定义,int(*p)()
赋值符=具有右结合性,+具有左结合性。

printf("%d\n",sizeof 'A')的值不是1,而是4,也就是int的长度值。为什么呢?因为char如果在表达式中,那么会自动隐式转换为int,而sizeof就是一个表达式,所以char会转换成int。记住,参数传递也是一个表达式。

为什么printf能够传进去不同的类型呢?因为不论传进去的是哪一个类型,函数从堆栈取出来的总是int类型的。printf的函数的原型是printf(const char * format,...);所以任何参数传进去都会导致参数提升。

隐式转换是语言的一种临时手段,把所有的操作数都转换为统一的长度简化了代码的生成。这样,压到堆栈中的参数都是同一长度的,所以运行时系统只需要知道参数的数目而不需要知道参数的个数。
函数原型的重要:如果我在一个文件sub.c里面定义了一个函数,banana(float d,char i){......},在main
.c中直接调用banana(10.0,3),则我printf("float=%f,char =%x \n",d,i);时会输出float=0,char=0,为什么会这样。因为在main.c文件里没有banana的函数原型,所以在传递参数的时候,float会变成double类型的进行压栈,而banana取的时候又是根据float取的,这样又会影响char的位置的读取,所以会造成错误。注意如果banana改为banana(char i,float d){......},则char是取得对的,而float则是取的错的。
解决的方法就是在main.c里面加上一句,extern void banana(float,char)。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics