关于c语言的总结

来源:互联网 发布:淘宝客需要什么软件 编辑:程序博客网 时间:2024/05/22 10:29

寒假把c primer plus 国嵌的c语言看完了 但是笔记只是记在本子上,然后一直没时间整理,今天有时间了,就发上来;在这里先声明一下:这里有很多是来自国嵌的收费视频的知识;  然后本人属于刚学习c语言,有很多的问题不了解,要是有问题,别喷我哈;


Sizeof这个函数一般来说大家用的时候都会是这样用的:

Char a[] = hello world!;

Sizeof(a);  就是说带着括号的这种方法,将自己要进行测量的数据用括号括起来;但是sizeof一定要用括号么??  当然不是,比如说 printf(%d \n, sizeof 6.2 ); 很多人看了可能第一反应就是少打了括号,但是实际上就是我没打括号,这样运行的结果8,为什么呢??  

对于sizeof这个函数,当对类型名进行计算的时候就比用用括号括起来,比如sizeof(int),而不能是sizeof int; 因为如果类型名可以不用括号括起来的话 sizeof int 那编译器就会认为这是sizeof修饰int的,就跟 usigned int一个意思,这样肯定是不行的,所以类型名就一定要加上();

而对于起来的变量就可以随意了,但是为了阅读方便起见,还是加上(),但是开始的时候对于sizeof 6.2 为什么输出是8的问题上想了半天,后来问了一下学长,翻了下书,才发现确实疏忽了,对于没用声明的小数,编译器都会认为是double类型。所以才会输出8

然后就是如果:

Int func() { printf(enter func function; \n);}

 

Void mian()

{

   Prinf(%d \n, sizeof(  func() ) );

}

结果是指输出4,并没有输出”enter func function;,原因是sizeof只是计算函数返回值的大小,并不会调用这个函数,所以不会输出enter func function;

这个原因是为什么呢 原因就是sizeof是编译器在编译的期间就要出现结果,而不是在运行的时候才测出的结果,所以有时候就可以利用sizeof来进行编译时间替换运行时间,虽然编译时间增长 但是运行时间减小

 

 

 

 

Enum这个枚举型:首先是如果(注意变量之间要用逗号‘,’进行分割)

Enum color

{

   GREEN

   BLUE

   RED

};  这里如果不对color这个“变量”里的“常量”进行赋值的话默认会是GREEN=0,然后依次比前一个大于一,如果BLUE=10的并且RED没有赋值的话,RED=11

这里需要声明的是对于enum里包含的常量的值都是int型,不能赋值一个float,但是char型可以,这个没必要解释了。

然后是enum声明的变量可以自加:

 

例如

Color aa;

for(aa=GREEN; aa<=RED; aa++);

 

#define宏:一定要记住就是他是预编译器完成的内容,就是进行简单的文本替换,替换完成后,整个c文件就不会再出现#define这个宏定义了;标红的是什么意思呢??例如

#define FUNC(a) (a>0 ? FUNC(a-1) : 0)

void main()

{

   Printf(%d, FUNC(12) );

}

如果对这句话进行编译,会出现什么呢?  会提示没有定义FUNC这个函数?? 为什么呢,因为在预处理器进行处理的时候会将main函数里的FUNC替换成(a>0 ? FUNC(a-1) : 0) 然后把#define这条宏定义就删除了,所以在后边的编译时就会发现FUNC()这个函数没有定义;

 

然后是

#define MINa,b(a < b) ? a : b

 

Int i = 1;

Int b = 3;

 

Printf(%d \n, MIN( i++, b));

Printf(%d \n, MIN( ++i, b));

都输出什么呢??第一个输出第二个输出3,愿意就是简单的替换

第一个替换成 ( i++ < b) ? I++ : b

第二个替换成 ( ++i < b) ? ++i : b

这下就能看出原理了吧??

 

#define中还有关于###的特殊用法:

#是把宏参数变成字符串:

#define FUNC(a,x) printf(%s,%d,#a,a(x));

注意 这里的替换成字符串是要加上双引号的 例如:

#include <stdio.h>

#include <math.h>

 

#define PR(X, ...) printf("Message " #X ": "_ _VA_ARGS_ _")

 

int main(void)

{

double x = 48;

double y;

y = sqrt(x);

PR(1,"x = %g\n", x);

}

他被翻译成:

int main(void)

{

 double x = 48;

 double y;

 y = sqrt(x);

 printf("Message " "1" ": "_ _VA_ARGS_ _");

}

 

Int kaifang(int x)

{ return x*x }

 

如果这样调用FUNC(kaifang2),那么会输出kaifang4;这就是宏的强大体现;

 

##是将两个符号黏在一起:

#define NAME(a) name#a

 

Int NAME(1);

INT NAME(2);

 

这样的话 这两句就等效于 int name1;

   Int name2;

 

如果这样:

Void func()

{

   #define MAX 100;

}

 

Void main()

{

   Printf(%d \n, MAX);

}

 

这个是不会出错的,因为#define 不会有局部变量的这一说法,所以只要记住#define之后就可以用了,不论他出现在什么地方,当然如果向

int func()

{

   #define MAX 100;

   Return 0;

   #undef;

}

 

Void main()

{

   Printf(%d \n, MAX);

}

这样就可以使MAX只在func这个函数里用了,有人好奇,#undefreturn之后用,会不会不管用呢??当然不会,想想,宏展开是最先进行的编译,所以还没有执行return 0 这条语句前,就已经把undef这条指令让编译器执行完了。

 

对于define定义的函数有什么优势呢??  

比如#define fucn( a, b ) { int temp = a; a = b; b = temp; }

Void  func(int *a, int *b) { int temp = *a; *a = *b; *b = temp; }

应该看出来了吧??所有原因就是因为define的原理就是简单的替代,并不会像函数一样,传参。

对于#define 可以定义一个函数块,但是define规定是一定要在一行内定义完成,但是如果内容过多,一行的话,不容易看,所以就可以利用 这个连接符;例如:

#include <stdio.h>

 

#define f(a,b) \

{ \

int temp = a;    \

a = b; \

b = temp; \

}

 

int main()

{

int a = 1;

int b = 2;

 

f( a , b );

 

printf("a=%d, b=%d\n", a,b);

 

return 0; 

}

结果是:

 

可以发现,我的f使用#define定义的,使用了连接符 这样更方便观察书是一个函数了,十分方便,并且注意我在调用的时候是 f(  a , b ) 两个参数中间是有空格的,但是编译器并没有报警,说明这样子是没问题的,这里需要记住。

 

Typedef:刚刚说了如果在定义函数的时候用了#define 这条语句,也可以在定义之后的地方调用定义的量,但是typedef不会像define一样,typedef 如果在函数中定义了,那么他就会使局部变量,在函数外是不能用的。

 

关于注释:

注释大家都知道,编译器都会忽略他的存在,但是如何忽略的呢??

Int/**/a,b;

In/**/t a,b;

这两个那个没有问题呢?? 答案是第一个,为什么呢??因为编辑器会将注释以空格代替;

 

 

关于指针的赋值问题:

Char* p = a;

Printf(a);

这两个会出现什么问题呢??  

首先是一个指针,被赋值成‘a’,这个可能没见过,但是大家应该见过这种:p = 112; 意思是p指针指向112这个地址的内存,其实‘a’是一样的 aint值是65 那么就相当于p = 65

Printf大家可以这样用一次:char *a = hello world!;  printf(s); 结果确实输出hello world 原因就是printf()函数实际的参数就是printf*p) 参数是一个指针,所以printf( ‘ a)也相当于让指针p = a; 但是操作系统中都是将内存的低地址让操作系统利用,所以这两句话 编译不会出问题,但是运行的时候就会出现错误。  但是语法上要理解什么意思。

 

对于编译器有一些宏,这里简单的说一下:

#error:用于生成一个编译错误消息,注意是编译的时候,并不是运行的时候!!!

例如:

#include <stdio.h>

 

#define CONST_NAME1 "CONST_NAME1"

#define CONST_NAME2 "CONST_NAME2"

 

int main()

{  

    #ifndef COMMAND

    #warning Compilation will be stoped ...

    #error No defined Constant Symbol COMMAND

    #endif

 

    printf("%s\n", COMMAND);

    printf("%s\n", CONST_NAME1);

    printf("%s\n", CONST_NAME2);

 

    return 0;

}

 

这个上比那如果没有定义COMMAND这个的话,那么在编译的时候就会出现错误并停止编译:

 

 

注意#warning这个也是一个编译指示子,用于在编译的时候进行报警使用,但不会让编译器停止编译,#error回事编译器停止编译。注意这两个区别;

 

然后如果我定义了COMMAND再看一下结果:

 

所以#error#warning这两个都属于编译指示字,用于对编译过程出现提示信息;

 

#pragma  这个一定要记住如果当c文件中包含这个文件的时候,千万不能直接在自己的编译器中进行编译,因为这个指示子的指令并不是所有的编译器都是别,每个编译器对它的指令都不相同,他并不喜爱那个#include ,#define,#line这些在编译器前,进行预处理(文本编辑),而是留给编译器,编译器如果是别,那么就进行这个指示子的动作,如果不是别呢?那么就跳过!!,记住并不会报错,而是跳过。

例如:#pragma message 就是单纯的消息提示,但是只有vc识别,在gcc中并不是别直接跳过了。

例如如下的程序:

#include <stdio.h>

 

#if defined(ANDROID20)

    #pragma message("Compile Android SDK 2.0...")

    #define VERSION "Android 2.0"

#elif defined(ANDROID23)

    #pragma message("Compile Android SDK 2.3...")

    #define VERSION "Android 2.3"

#elif defined(ANDROID40)

    #pragma message("Compile Android SDK 4.0...")

    #define VERSION "Android 4.0"

#else

    #error Compile Version is not provided!

#endif

 

int main()

{

    printf("%s\n", VERSION);

 

    return 0;

}

 

vc中如果定义了ANDROID23,那么在编译的时候会提示出Compile Android SDK 2.3...,这样就可以清楚的知道自己在编译的是那个文件了。

而在gcc中由于不识别这个指令,所以不会有任何反应。

 

看到了吧,如果我们不定义任何信息,也会报错,但是如果我们定义了ANDROID20 虽然通过编译,但是并不会在编译的时候出现提示信息,运行的时候,的确执行了#define VERSION "Android 2.0"这条语句,所以,#pragma这个一定要记住,不是所有编译器都是别;

 

 

对于#pragma这个有一个命令:

#pragma pack(2)  2字节对齐   

#pragma pack(4)  4字节对齐

这是告诉编译器要以几个字节对齐,但是要注意虽然你指定了这个几个字节,但是不一定成功;

 

 

这里先说一下编译器编译的大概过程:

 

这里的预处理就是:

处理所有的注释, ,以空格代替 以空格代替

将所有的#define删除, ,并且展开所有的宏定义 并且展开所有的宏定义

处理条件编译指令#if, #ifdef, #elif, #else, #endif

处理#include, ,展开被包含的文件 展开被包含的文件

保留编译器需要使用的#pragma指令

 

然后编译器进行编译:

对预处理文件 进行一系列词法分析, ,语法分析和语义分析 语法分析和语义分析

?词法分析主要分析关键字, ,标示符 标示符, ,立即数等是否合法 立即数等是否合法

?语法分析主要分析表达式是否遵循语法规则

?语义分析在语法分析的基础上进一步分析表达式是否合法

?分析结束后进行代码优化生成相应的汇编代码文件

 

然后生成汇编;

 

#line 这个也是一个编译器的处理命令:他的作用是强制指定新的行号和文件的名字;(这里需要注意的是,新的行号是指的定义完后的下一行行号是当前指定的行号)

例如:

#include <stdio.h>

 

#line 14 "Hello.c"

 

#define CONST_NAME1 "CONST_NAME1"

#define CONST_NAME2 "CONST_NAME2"

 

void f()

{

    return 0;

}

 

int main()

{

    printf("%s\n", CONST_NAME1);

    printf("%s\n", CONST_NAME2);

    printf("%d\n", __LINE__);

    printf("%s\n", __FILE__);

    

    f();

 

    return 0;

}

他的运行结果就是

 

并且文件的名字也已经改成了Hello.c这个名字了(这里原来文件的名字叫test.c

 

关于数组:

Int a[5];

 

这里 指的是a[0]这个元素的地址;

但是 &a 指的是a[5]这整个一个大数组的首地址,是整个大数组;

但是有个东西需要知道:

就是关于sizeof 测量数组所占的字节大小:

 

#include <stdio.h>

 

int main()

{

int a[5] = {1,2,3,4,5};

 

printf("sizeof(a)= %d, sizeof(&a)= %d  \n", sizeof(a), sizeof(&a));

 

printf("a=  %0X, &a=  %0X\n", a,&a);

printf("a+1=%0X, &a+1=%0X\n", a+1,&a+1);

 

return 0; 

}

 

他的结果是:

 

看到了吧 sizeof(a) 20个字节, 但是sizeof(&a) 却是4个字节;

 

关于数组名,我们总是下意识的那他当作指针来进行计算,但是数组名和指针有一个本质的区别这个一定要知道,就是

对于指针,编译器是先取出指针所在地址里存的地址值,然后再这个地址去找这个内存里的值。

而数组名不一样,数组名的自身地址开始算数组的开始地址,而并不是数组名的地址里存的值是数组的开始地址。

这两个是不同的,一定要明白;

 

例如这样一个例子:

another.c中有这样一句: char *p = hello world!;

现在test.c要调用another.c中的p指针,所以要进行声明,但是是这样声明的:extern p[];

那要想读出hello world这句话,如何读呢??

printf("%s \n", ((char*)(*(unsigned int*)p)) );  

 

看到p是指针,所以占4个字节,4个字节的内容指向hello world这个字符串。但是由于我们在test.c文件中是将p声明成了数组,如果直接用prinf(%s \n, p); 那么结果是乱码,原因就是他会将p当前的地址当作这个数组的开始,然后直接将这四个字节里的每个值当作字符输出,所以当然会错误,  所以我们首先应该将p这四个字节整体的值取出来,  我开始想的是直接(unsigned int)p  这样强制转换,但后来发现 这样肯定不可以,因为p的值是这个四字节数组的开始地址值,(unsigned intp  是将地址值转成了unsigned int 型, 所以应该是先将(unsigned int*这样p就指向了这块四字节内存的指针了,然后提取出来,就可以了 就是图上边的printf这条语句;

 

关于数组的类型,它包括两个部分,一个是元素的类型另一个是数组的大小;例如:int a[5] 

他的类型就是int[5];那么如何通过typedef 来定义一个数组呢?/

 

例如我现在:

Typeddef  int(IAI5)[5];

IAI5 a; //这个a就是5个元素并且每个元素都是int类型的数组

 

关于数组的指针:

Int a[5];

Int *p = a;

这里的p并不是指向数组a, p还是指向int型的指针,这个要记住。

那如何定义一个指向数组的指针呢?有两种方法:

1: typedef int(Arry)[5];

   Arry* pointer = &a;

2: int (*pointer)[5] = &a;

上边这两种方法最后指针都指向了a这个数组;

 

关于数组的赋值,例如要将数组里的元素分别赋值成1 2 3 4 5

当然有两种方法:

方法一:

For( i=0; i<5; i++)

{

   a[i] = i;

}

 

方法二:

Int *p = a;

For( i=0;  i<5;  i++)

{

   *p++ = i;

}

这两种方法虽然作用都一样,但是方法二的工作效率要比方法一高很多。

 

对于二维数组:int a[2][3];

这里的 &a 代表的是整个二维数组;

(&a +1) - &a = 2×3×4

(a + 1) - a = 3×4

 

 

关于函数:

函数的类型:包括返回类型,参数个数,以及每个参数的类型;

如果用typedef 来进行定义的话方法如:

Typedef  type name (参数类表);  (type 指的是函数返回类型,  name指的是名字)

例如: typedef int f (int , int);

定义函数指针有两种方法:

第一种:

typedef int Func (int , int);

Func* pointer;

第二种:

Type (*pointer)(参数列表)

 

对于函数指针经常出现下面两种使用方法:

Void test(int, int); // 函数声明

Void (*pointer)(int, int) = test;

Void (*pointer)(int, int) = &test;

虽然第二种在了但是作用都是一样的,加&这是以前的用法,现在可以直接使用函数名即可;

定义了函数指针后,如何进行函数的调用呢?也是两种方法:

1: pointer(    ,    );

2:   (*pointer)(   ,   );

这两种方法一样,任选其一;

 

对于一些复杂的指针,可以用右左法则进行读取:

1. 从最里层的圆括号中未定义的标示符看起

2. 首先往右看, ,再往左看 再往左看

3. 当遇到圆括号或者方括号时可以确定部分类型,

并调转方向

重复2,3步骤, ,直到阅读结束

 

1

Void fun(i,j)

{

   Printf(%d, %d, i,j);

}

 

Void main()

{

   Int k = 1;

   

   Fun(k, k++);

}

2

Int i = f() * g();

 

这里放着两个例子的原因是因为例1fun函数的参数 和 k++ 计算顺序不知道, 例2的 f() 和 g() 这两个函数也不知道是先算的那一个。  

也就是说,在c语言里,并没有规定函数的参数是按从左到右一个一个运算还是从右到左一个一个运算,

这个时候,像例1中的 funk, k++); 还有int k =2;  k = k++ +  k++; 这些编译器是按照一个规律进行运算,就是寻找数序点,顺序点有3个,分别是:

1:分号 

2: &&|| 、 ? : 、 以及逗号;

3: 当函数的参数全部完成运算之后,在进入函数体之前;

 

由于文字不好解释,就略过了;  

但是有一点需要注意:就是当我们编程时可能会利用到别人写过的库,有些库可能别人已经编译好了,不需要我们自己的编译器重新编译,这个时候就会有可能出现一个问题,那就是关于函数参数在栈中的排列顺序,可能在作者的编译器中函数的参数在栈中的排列是按从左到右的顺序,而在我们自己的编译器中参数是从右到左的顺序,这样就会出现问题,这个时候我们就要让编译器按照作者的编译器规则将参数放到栈中。

 

关于函数的参数,有一个我一直都不知道的问题,就是函数的参数数量可以不限制,叫做可变参数;  而这个参数可变函数的实现要依赖于  stdarg.h 这个头文件才可以实现;

 

#include <stdio.h>

#include <stdarg.h>

 

float average(int n, ...)

{

    va_list args;   // 定义可变参数列表

    int i = 0;

    float sum = 0;

    

    va_start(args, n);   //开始读取可变参数列表

    

    for(i=0; i<n; i++)

    {

        sum += va_arg(args, int);  // 读取参数值  注意要注明取的类型;

    }

    

    va_end(args);   //  读取完毕 

    

    return sum / n;

}

 

int main()

{

    printf("%f\n", average(5, 1, 2, 3, 4, 5));

    printf("%f\n", average(4, 1, 2, 3, 4));

    

    return 0;

}

 

 

 

这里要说一点关于可变参数的限制:  必须从头到尾进行顺序访问

   参数列表中至少要存在一个确定的命名参数

   无法确定实际的参数个数

   无法判断参数的类型(所以不要以错误的类型进行读取 会出现错误)

 

 

程序的内存分配:

关于程序的内存分配问题,是这样的,当程序没有运行时,可执行文件的内存分配是:

 

而程序在运行的时候的内存分配是:

 

 

对于局部变量,都是保存在栈中,而动态申请的内存是从堆中申请的,为什么栈在最前呢,因为要调用main函数,会有一些临时变量,所以要最先建立栈,只读存储区的数据不能修改,例如:char *s = hello world!这里的s就是指向只读存储区,不能修改hello world 这句话里的内容;

 

对于栈:是用来维护函数调用,没有栈,就不会有局部变量,当函数调用结束后,栈就自然会销毁;

对于堆来说,堆是为malloc 这些申请动态内存准备的,如果在函数中申请完后,函数结束,申请的内存并不会被销毁,这个是跟栈不同的。

并且系统对于堆的管理方式: 空闲链表法、位图法、对象池法等等;这些各有各的优点,没有谁最好;

Malloc( sizeof(int) ); 理论上他是申请4字节的内存,但是实际上不一定是4字节,为什么呢?因为我们c语言是一个很高效的语言,对于堆的管理是分成一份一份的,当你申请4个字节的动态内存的时候,他会立即寻找,可能没有正好4字节的内存,但是有5字节的内存块,他就会直接把这5个字节的内存块分给申请者,并且不会将这5个字节拆成4个字节和1个字节。但是多出的那一个字节我们也是没法利用的。

 

既然说道动态申请内存,就说一下几个申请内存函数:

首先是用的最多的 malloc 这个不用说,

然后还有calloc: void* calloc( size_t num, size_t size)  这里的第一个参数是申请的单元数量,第二个参数是每个单元的大小,这个函数相对于 malloc 的优点是申请完的内存都已经初始化为0;(这里注意 因为申请完了的每个字节都是初始化为0, 所以如果申请的是一个结构体的内存大小, 结构体中的每个元素的值也为);

还有就是 realloc : void* realloc(void* pointer, size_t new_size); 这个函数的第一个参数是指向当前需要修改大小的内存指针, 第二个是要修改的大小。 如果第一个参数是 NULL 那么这个函数就跟 malloc 这个函数一个作用没有区别了。

 

说一下strlen 这个函数: 

有如下例子:

 

会发现为什么还是输出 a > b 呢??  原因是 strlen 这个函数返回的类型是unsigned int 无符号类型, 不论怎么做加减, 他永远都会大于零, 所以肯定会有问题,所以strlen 千万不要用上述方法;我后来又看了一些其他函数 比如: sizeof()  等等 他们为了能把长度表达到最大值都是将返回值设置成了无符号的,所以以后使用这些函数一定要记住。

 

说一下strcpy这个函数:

如果被复制的数组的空间大于要复制过去的数组空间,程序就会出错,所以这个一定要避免

对于这个可以使用strncpy(char *des, char *src, size_t num);这个函数,这个函数的num指的要从源中复制多少到目标里去,这里也有几点需要注意,否则也会出现问题:

1

int main()

{

char s[] = "1234";

char d[] = "654321";

strncpy(d, s, 4);

printf("%s\n", d);

return 0;

}

这里的num如果小于s的长度5的话,由于stncpy并不会把结束符复制进去,所以会出现输出的时候输出的是123421这种情况;所以最后等于源的长度否则会出问题;

 

再如果下面的情况:

#include "stdio.h"

#include "string.h"

int main()

{

char s[] = "1234567890";

char d[] = "654321";

printf("sizeof(d)=%d\n", sizeof(d));

printf("s=%s\n", s);

printf("d=%s\n", d);

strncpy(d, s, 13);

printf("sizeof(d)=%d\n", sizeof(d));

printf("s=%s\n", s);

printf("d=%s\n", d);

return 0;

}

这里的最后一个s输出的是890,而不是1234567890,为什么呢??因为栈是从高地址向低地址生长的,这里有个情况说一下:

首先是strncpy这里有个13,这个是大于s的长度11的,这里strncpy 会看\013那个先到,哪个先到就停止复制了。

然后就是因为s的数组长度已经大于d的长度了,所以会将s数组后面的内容进行了覆盖,结果导致s的内容发生了覆盖,把1234覆盖了,变成了890 \0了,如果不信可以自己将sd的首地址输出一下就明白了 

然后就是sizeof的问题了 为什么两次输出的都是7呢?? 明明后来的d\0出现在了第11位上了,这是因为sizeof 是在编译的时候就已经算出答案了,那个时候程序还没有运行,所以d的数组并没有发生变化。

所以最好把长度设置成源的长度,避免出现你不想要的现象。

 

数组的类型:是由元素的类型哦数组的大小共同决定的:

例如:int array[5] 它的类型: int [5]

 

这样定义一个数组的话就可以通过:

Typedef  int(AINT5)[5];

AINT5 iarray; //这个iarray就是一个5个元素并且每个元素都是Int类型的数组了

而定义一个指向数组的指针可以是:

Typedef int(ArrayType)[5];

ArrayType *p = &a;

或者是:

Int (*p)[5];

 

对于数组的赋值,有两种方法:

第一种:

For(int i=0; i<n; i++)

{

a[i] = i;

}

第二种是:

For(int i=0; i<n; i++)

{

*p++ = i;

}

这两种里面第二种效率要比第一个高;

 

对于二维数组:

Int a[2][3];

 

(&a + 1) - &a  = 2*3*4;

(a + 1) - a = 3*4

注意这两个的区别

 

函数类型:返回类型, 参数个数, 以及每个参数的类型;

Typedef type name(参数类型)

 

Typedef int f (int , int);//这里的f是一个代表返回类型是int类型, 有两个参数并且都是Int类型的函数;

 

函数指针:

Void test(int, int);

Void (*p)(int, int) = test;

Void (*p)(int , int) = &test;

这两种赋值的方法都是一样的,

在调用的时候:

P(), 与(*p()也都是一样的,没有区别; 

 

作为整数, 如果是10位的话,那么就设置成long long类型

 

Char *s = “123”;

 

 

下面是我看c primer plus中发现的一些东西,以前一直没有注意过:

Printf(); 这个函数是有返回值的,当输出错误的时候会返回负数。

然后是有一种用法,虽然都是不常用的,毕竟是为笔试准备,都知道比不知道强。

printf("123""456\n");  这里是两个字符串变成一个字符串,然后输出,但是一定要注意,两个字符串之间要是有逗号的话,就只能输出第一个字符串了,不会吧他们合并了。

同样 

char b[] = "ssdfg""hell world";

printf("%s\n", b);这里也会把两个字符串合并成一个字符串

 

对于scanf() 函数, 他的返回类型如果成功读入,那么返回读入的项目数

如果读的是文件爱你读到结尾,那么返回EOF

 

关于取模运算符: 如果第一个操作符是负数, 那么取模后也为负数;

11%-2 = 1

-11 % -5 = -1

-11 % 5 = -2

所以通过以上的结果可以发现,取模运算符的结果的正负只与第一个操作数的正负值有关系;

 

For语句,我们通常都是定义变量, 然后就是对变量进行运算, 但是也可以这样;

for(printf("first\n"); printf("second\n"), i<3; printf("third\n"), i++);

他会按照正常顺序,并且将first, second, third一次输出;

 

 

c语言中定义一个函数:

void a()

{

}

在调用的时候: a(12);这个是可以通过的,不会对参数进行检查,

但是

void a(void)

{

}

这个时候在a(12)就会出现错误,在c语言中有区别,但是在c++就要严格了,这两种定义,在调用的时候都不能有参数,否则会报错;

 

对于数组,在开始定义的时候进行初始化:这个很正常,但是我想在定义的时候便给某个元素进行赋值这个是如何使用呢?

Zai c99出现之前,这个是不可以做的,但是现在可以了:

int a[10] = { [3]=6, [9] = 1 };

int i = 0;

for(i=0; i<10; i++)

{

printf("%d\n", a[i]);

}

 

这里的【3】 = 6 意思就是a[3] = 6 而其他的元素便会赋值为0了;

int a[10] = { 31, 32, [4]=30, 35, 36, [1]=29 };

int i = 0;

for(i=0; i<10; i++)

{

printf("%d\n", a[i]);

}

 

这里的输出结果是:

31

29

0

0

30

35

36

0

0

0

这里的a[1]=29的原因是因为虽然在开始赋值了32 但最后又修改为了29  所以以最后一次的为准;

 

复合字:这个是第一次看到的, 这个也是在c99之后出现的:

以前定义数组的方法是: 

int a[3] = {1,2,3};

 

但现在可以(int [3]){1,2,3};

但是因为现在这种定义没有数组名,所以没法直接引用, 所以使用的方法只有两种:

第一种: int *p = (int [3]) {1,2 ,3};

第二种作为函数参数: 声明一个函数:void fun(int a[],  int n);

在调用的时候:fun( (int [3]){1,2,3}, 3 );

 

定义二维数组也可以使用复合字:

int 2】【3】){ {1,2,3}, {4,5,6} };

 

说一下fgets(), 大家都知道,这个是读取文件的函数,但是也可以读取键盘:

第二个参数表示要读入的字符数,第三个是要读哪个文件,当要从键盘读取的时候,可以使用stdin,就表示要从键盘中读取;如果还没有读够这么多的字符,但是读到了换行符,也会停止,但是他会把换行符存到字符串中。

 

Puts()printf()的区别在于puts() 函数会自动加上换行符

 

 

 

关于字符串的处理函数:

 

Strcat(): 两个字符串参数, 将第二个字符串拷贝到第一个字符串的结尾,但是并不会检查第一个数组是否可以容纳第二个字符串。

 

Strncat(): 第三个参数指明最多允许添加的字符数;但是如果碰到了结束符,那么提前结束;

 

Strcpy() : 返回的是被复制的字符串

第一个参数可以不是从数组的头开始的;

例如:

char *org = "beast 123";

char copy[] = "be the best that you can be";

char *ps;

ps = strcpy(copy+7, org);

puts(copy);

puts(ps);

*ps = 'A';

puts(copy);

puts(ps);

 

上面说过现在数组可以在定义的时候指定个别的值对他进行赋值,结构体也一样:

struct book

{

char name[20];

int price;

char witter[20];

};

 

int main()

{

struct book b1 = { .name="c lan", .witter="qiuchang", };

printf("%s , %d, %s\n", b1.name, b1.price, b1.witter);

return 0;

}

 

由于以前确实用到结构体的时候不是太多,当然数据结构除外哈, 有一点老是不确定:

就是:结构体可以直接相互赋值

例如:

 

Struct book b = a;

这里这种赋值,如果成员中的数组也会直接复制过去。这里要注意就是如果结构体含有指针,那么就有可能会出现问题了:

 

struct book

{

char name[20];

int *price;

char witter[20];

};

 

int main()

{

int a = 10;

struct book b1 = { .name="c lan", .witter="qiuchang", .price = &a};

struct book b2 = b1;

printf("%s , %d, %s\n", b1.name, *b1.price, b1.witter);

printf("%s , %d, %s\n", b2.name, *b2.price, b2.witter);

*b2.price = 0;

printf("%s , %d, %s\n", b1.name, *b1.price, b1.witter);

printf("%s , %d, %s\n", b2.name, *b2.price, b2.witter);

return 0;

}

 

 

 


0 0
原创粉丝点击