C++语法笔记(三)

来源:互联网 发布:淘宝恶意刷流量给同行 编辑:程序博客网 时间:2024/05/03 23:47

数据类型

输入输出格式  %符号说明符

%d有符号十进制整数  %ld有符号十进制长整数

%lld64位长整数定义用long long

%u 无符号十进制整数  %lu 无符号十进制长整数

%o 无符号八进制整数  %x无符号十六进制整数 字母用小写; %X 无符号十六进制整数,字母大写

%c字符型    %s字符串型

%f有符号十进制浮点数float   %lf有符号十进制双精度型double

 

 %Lf有符号十进制长双精度型

%e 有符号十进制浮点型 %E 有符号十进制浮点型

 

单个字符的输入输出: getchar(),   putchar()

puts(字符串), 在输出时将字符串结束标志’\0’变为 ‘\n’,即完成字符串输出后换行.gets(source)键盘直接输入给source.

.gets(字符串), 读入字符串到对应的字符数组中,并且而返回一个函数值,它是字符串变量所对应的字符数组的起始地址。

.strlwr(字符串)将字符串变量中的大写字母转换成小写字母,返回地址。

.strupr(字符串)将字符串变量中的小写字母转换成大写字母

字符’’, 字符串””,  char 的范围为-128~127,

sprintf把格式化的数据写入某个字符串缓冲区。

 

C语言不支持函数重载,C++支持函数重载。

头文件:

stdio.h

函数原型:

int sprintf( char *buffer, const char *format,[ argument] … );

参数列表:

buffer:char型指针,指向将要写入的字符串的缓冲区。

format:char型指针,指向的内存里面存放的将要格式字符串。

[argument]...:可选参数,可以是任何类型的数据。

返回值:字符串长度(strlen)

 

sscanf()- 从一个字符串中读进与指定格式相符的数据.

函数原型:

int sscanf( constchar *, const char *, ...);

int sscanf(constchar *buffer,const char *format,[argument ]...);

buffer 存储的数据

format 格式控制字符串

argument 选择性设定字符串

sscanf会从buffer里读进数据,依照argument的设定将数据写回。

 

fgets(buf, MAXLEN, fin)将读取完整的一行放到字符数组buf中。

 

 

.main函数为程序最先执行的函数,函数之间没有相互包含的关系。

C语言可以将任意指向字符串的指针看做是字符串。

变量类型

(一)局部变量

1、auto  变量类型缺省为auto,只在说明它的语句或块中才有意义, 离开作用域后值消失

2、register   用法和作用域同auto

3、static  静态变量,作用域局限于说明他们的函数或块, 但在离开作用域后其值不变。C语言缺省约定,定义静态变量时没有初始化时,自动赋为0.

(二)全局变量

在程序的首部加以定义,作用域为全局。静态存储分配,如果没有赋初值,自动赋值为0.

 

运算符

%取模运算符,   != 不等于   !非  &&与  ||或

逗号运算符逗号运算符的作用是将若干表达式连接起来。它的优先级别在所有运算符中是最低的,结合方向是"自左至右"的。其值是最后一个表达式的。

s=2;   d=3;  a=12+(s+2,d+4);

先算s+2.的4,然后算d+4,最后逗号表达是式的值是d+4=7,整个表达式的值是12+7=19,

 

 

         if (a> b){ //a b交换?

                   inttemp = a;

                   a = b;

                   b = temp;

         }

         inttemp;

         if (a> b)  //¨??¬?t¨¦¢¡éê?¨°¡äD??¨y?ê?¤?¨°?y̨²°??ê?¡À¨®¡äD¨®¢??¡ê

                   temp = a;

                   a = b;

                   b = temp;

c = a, a = b, b = c;//为一条语句

c = a;a = b; b = c;//为三条语句 ,一次执行完

 

条件表达式: printf(“maxof   %d , %d  is %d\n”, a, b,  (a>b)?a : b); //选出其中的最大者

 

.goto 语句   //尽量不用或者少用goto 语句

int main(){

         int i =1, s = 0;

loop:

         if (i<= 100){

                   s += i;

                   ++i;

                   gotoloop;

         }

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

         return0;

}

 

 

输入输出

         scanf("%d",&a);          printf("%d   %d", a, b);

scanf输入数据时候,会自动将之前的空格符滤掉, (char类型的除外,直接当做要输入的字符了)

 

宏定义

#define pi 3.1415926  //宏定义命令

如果一个宏定义在一行写不下,则可以“续行”, 这里是通过在行尾加上一个  ’\’  来实现。

#define pi 3.1\

4159\

26

带有参数的宏替换(为避免意想不到的bug, 最好每个参数都用括号括起来)

#define   area(x)   (pi*x*x)    //最好为#define   area(x)    ((pi)*(x)*(x)) 

带有参数的宏调用是在编译阶段完成的,而带参数的函数调用是在程序运行的时候完成的。

 

取消宏定义   #undef 宏名

文件包含

#incldue<文件名> 不在当前的目录中寻找,直接到系统预先设定的目录中寻找;

#include”文件名” 先在当前目录中寻找,找不到再去系统设定的目录中去寻找。

 

条件编译

#if 常量表达式   //注意常量表达式是在编译时求值的,因此,只能由事先定义的宏名和常//量组成。

 

程序段

#endif

 如果常量表达式为真(非零),则相应的程序段被编译,否则不被编译。

 

#ifdef 宏名

程序段

#endif

如果已定义了相应的宏名,则编译相应的程序段,否则不会编译程序段。

 

#ifndef 宏名

程序段

#endif

如果没有相应的宏名,则编译相应的程序段,否则跳过它。

 

 

文件中的#ifndef 
头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。 
还是把头文件的内容都放在#ifndef#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的: 
#ifndef <
标识
#define <
标识
...... 

...... 
#endif 
<标识>在理论上来说可以是自由命名的,但每个头文件的这个标识都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h 
#ifndef _STDIO_H_ 

#define _STDIO_H_ 
...... 
#endif 

 

 

2.#ifndef中定义变量出现的问题(一般不定义在#ifndef中)。
#ifndef AAA
#define AAA
...
int i;
...
#endif
里面有一个变量定义
vc中链接时就出现了i重复定义的错误,而在c中成功编译。
结论:
(1).
当你第一个使用这个头的.cpp文件生成.obj的时候,int i 在里面定义了当另外一个使用这个的.cpp再次[单独]生成.obj的时候,int i 又被定义然后两个obj被另外一个.cppinclude这个头的,连接在一起,就会出现重复定义.
(2).
把源程序文件扩展名改成.c后,VC按照C语言的语法对源程序进行编译,而不是C++。在C语言中,若是遇到多个int i,则自动认为其中一个是定义,其他的是声明。
(3).C
语言和C++语言连接结果不同,可能(猜测)时在进行编译的时候,C++语言将全局
变量默认为强符号,所以连接出错。C语言则依照是否初始化进行强弱的判断的。(参考)
解决方法:
(1).
把源程序文件扩展名改成.c
(2).
推荐解决方案:
.h
中只声明 extern inti;.cpp中定义
<x.h>
#ifndef __X_H__
#define __X_H__
extern int i;
#endif //__X_H__
<x.c>
int i;
注意问题:
(1).
变量一般不要定义在.h文件中。
一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是条件编译。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。 
条件编译命令最常见的形式为: 
    #ifdef
标识符 
   
程序段
    #else 

    程序段
    #endif 

     
    它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2 
   
其中#else部分也可以没有,即: 
    #ifdef 

    程序段
    #denif 

指针和地址 引用

.x = = *(&x) 

指针数组

K维指针数组定义为 类型 *数组名[N1][N2]….[Nk]

int* a[4] 指针数组

表示:数组a中的元素都为int型指针

元素表示:*a[i] *(a[i])是一样的,因为[]优先级高于*

int (*a)[4] 数组指针

表示:指向数组a的指针 ,数组a为整数数组,一维四个元素,指针a指向它  !!!

元素表示:(*a)[i]

 

 

C语言中字符串变量是用一位字符数组名来表示。 字符串变量实际是一个字符指针,指向一个以’\0’为结尾的字符串的首字符。一个字符指针可以看作是一个字符串,该字符串从该字符指针所指的字符开始知道遇到’\0’结束。

char str[10] = “abcd";  或    charstr[10] = {'f','d','e', 'f'};

而不可以          charstr[10];

                            str= "jfifdf";   这样是不对的,而应该用strcpy(str, "jfifdf");

char *p;         p = "fdkjfdf";    或   char *p = "fdkjfdf";

 

char str[5] = “abcd”; 此时”abcd”为字符串变量, 因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为
char str[5] = {'a','b','c', ‘d’};

 

char *p = “abc” 此时”abc”为字符串常量, 因为定义的是一个普通指针,并没有定义空间来存放"abcd",所以编译器得帮我们找地方来放"abc",显然,把这里的"abc"当成常量并把它放到程序的常量区是编译器最合适的选择。所以尽管p的类型不是const char*,并且p[0] = 'x';也能编译通过,但是执行p[0] = 'x';就会发生运行时异常,因为这个语句试图去修改程序常量区中的东西。

 

字符串常量实际上是一个字符指针常量,它指向该字符串常量的首字符,故可以将其赋值给一个字符指针变量,一维字符数组名是一个字符指针常量,故字符串常量不能赋值给它。

 

返回指针的函数      类型 *函数名(形参列表)  int * han( inta, char b) //函数返回一个int型指针结果

指向函数的指针      类型(*变量名)()

如 int(*p)  ( ) // 定义了一个函数指针变量p. 它是一个int型函数指针变量,即该变量应指向一个返回int型值的函数

int main(){

         char*fun(); //º?¨¨¦¨´¡Â¡¥ºy

         char*(*p)(); //pº??¨°¤¦Ì?char*¨ª¦Ì¡¥ºyÌ?¡¥ºy??À?¢?

         p = fun; //???p3¦Ìafun

         puts((*p)()); //º??

         return0;

}

char * fun(){

         return "this is my book";

}

 

 

命令行参数

传递给main函数的参数可以在操作系统的命令行提供,这种参数叫做命令行参数。

.main(int argc, char* argv[])  //argc表示接受命令行参数的个数; argv[] 字符指针数组,其每个元素依次存放着指向相应命令行参数字符串的首指针

int main(int argc,char*argv[]){

         int i;

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

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

                   printf("argv[%d] = %s\n", i, argv[i]);

         return0;

}

结果:

E:\材料\visual studio 2010\C\Jason_C\Debug>    输入 命令行 Jason_C jk fd dk

argc = 4

argv[0] = Jason_C

argv[1] = jk

argv[2] = fd

argv[3] = dk

 

结构和类型

结构体成员访问通过  . 来实现,操作符从左到右结合。

Student.age = 13;

只有当结构变量为全局变量或静态变量时,才能在定义时对其进行初始化。

 

如果用struct Pri

{

 charch;

 intpri;

};  Prilpri[]={{'=',0},{'(',1},{'*',5},{'/',5},{'+',3},{'-',3},{')',6}};

纯C的话不行,Pri是结构体名而不是类型名,会编译出错。C++下会把Pri当作struct Pri,不会出错。即在C中,每个结构体变量的定义或声明之前都要加 struct,对变量的内部成员的访问除外。

可以这么写:

typedef struct

{

char ch;

int pri;

}Pri;

Prilpri[]={{'=',0},{'(',1},{'*',5},{'/',5},{'+',3},{'-',3},{')',6}};

 

或者

struct student{

       int age;

       char *gender;

};

struct student stu[2] = {{32, "fjd"},{3, "fd"}};

 

 

结构指针和成员访问

 struct student *p;

       p = &stu[0];

       (*p).age = 10, p -> gender = "male";

 

结构体自引用:结构体变量不可以作为结构体的内部成员,但是结构体变量指针可以作为结构体的内部成员。

 

联合union(极少用)

和结构体struct差不多,结构体的各个成员分别具有各自的存储空间,而联合union的各个成员共享一块存储空间。但union的成员引用时,只有一个成员变量起作用,其他的都被屏蔽。

位域:以二进制为存储单位的结构成员。(为节省空间,也较少用)

struct person{

       char name[21];

       unsigned sex:1;

       unsigned age:7;

};

sizeof(struct person)  应该为22个字节。

 

枚举类型enum(很少用)

相当于自定义了一套变量类型。

 

.typedef 将某种结构定为 xx 类型,用来方便的定义变量。

 

位运算

 

&按位与,  |按位或, ^按位异或, ~按位取反,<<左移,  >>右移

X &= y 相当于 x = x&y;

 

输入输出

 

.getchar(), putchar(),  gets(),  puts(), printf(), scanf().

printf格式说明符

%[flag][width][.prec][size]type_char

.flag标志符 说明对齐方式、符号、结尾零等。

.width 宽度  说明输出的最小宽度,以字符为单位

.prec 精度  说明输出的最小精度,以字符为单位

.size 尺寸 说明对应输出参数的尺寸

 

.flag:

‘ - ‘输出结果左对齐,并再有段补齐空格, 缺省是输出结果右对齐,并再左端补满0或空格

‘+’总是在输出结果前面加上 + 或 – 号

‘ ‘空格 : 如果输出结果非负,则在其前面加个空格, 否则在前面加个负号

.width:

.n 至少输出n个字符,如果输出值少于n个字符,则用空格补满余下的位置(如果flag为’-‘,则在右端补,否则在左端补)

.0n 至少输出n个字符,如果输出值少于n个字符,则在左端补满0.

.*输出字符个数由下一个输出参数指定。

.prec:

无  系统缺省精度

.n 输出n个字符或n位小数部分,如果要输出的值多于n个字符,则将其截断或四舍五入

 

Scanf符号说明符

%[*][width][size] type_char

 

文件IO

C语言将所有的文件(正文文件、二进制文件、设备文件)都作为一个字符流来看待—流式文件,最小的存取单位为字节。

FILE文件结构类型在”stdio.h”中定义出了。

1、打开文件:

 

       FILE *p;

       p = fopen(char *filename, char*mode);

mode的几种类型:

r 打开文件,只能进行读操作。

w 创建filename指明的文件,如果已存在,则覆盖之,只能进行写操作。

a 创建或打开文件, 只能在其后面增加内容。

r+ 以读写方式打开由filename指明的文件

w+创建由filename指明的文件,如果该文件已经存在,则覆盖之,然后可对其进行读写。

a+ 创建或打开由filename指明的文件,只能对其尾部追加内容,但可对新追加的内容进行读写。

为了指明文件是正文文件(text  file),可在mode后加t  如w+t,  rt

为了指明文件是二进制文件(binary  file),可在mode后加b  如w+b,  rb

 

FILE * fp = fopen("E:\?¢?\visual studio2010\C\Jason_C\test.txt","r");//vs 2010中要放在程序的前面,否则有问题??

Fclose(fp);

2、文件读写

单字符输入输出  getc() putc()

Int getc(FILE* p)    int putc(int c, FILE *p)

 

行输入输出  fgets()  fputs()

.char *fgets(char *s, int n, FILE *p)从fp所指的输入流文件的当前位置读取若干个字符到字符串s中,它在读入n-1个字符或遇到第一个换行符’\n’后结束。

.int fputs(char *s, FILE*p); 向fp指向的文件写入以’\0’结尾的字符串s,但空字符\0’不写入。如果成功,fputs()返回写入的字符,否则返回EOF

 

按格式输入输出 fprintf()  fscanf()

Int fprintf(FILE *p, char *format[, .. ]);

Int fscanf(FILE *p, char *format[, … ,address..])

通过标准输入输出设备进行文件输入输出:

fprintf(stdout, format, argument_1,argument_2…);

fscanf(stdin, format, address_1, address_2…);

 

feof 文件结束判断  int feof(FILE* fp) 如果结束返回一个非零值,否则返回0

ungetc() 回推字符  int ungetc(char c, FILE *p) 将字符c推回到fp所指的流文件中,下次getc时候读取该字符。 如果成功推回,返回字符c, 否则返回EOF

rewind 回绕文件 void rewind(FILE*fp) 将使文件指针fp 所指的流文件的当前位置指针重新指向该文件的开头。

.ferror 出错判断,判断对文件指针fp的最后一次读写操作是否出错,若出错,返回非零值。

.ftell 返回文件的当前位置 longftell(FILE *fp)

.fseek() 设置当前位置, intfseek(FILE *fp, long offset, int whence)

新的当前位置是通过参照点(whence)和相对于参照点的位移量offset来确定的。Offset以字节为单位。  参照点whence  = 0(文件开头) = 1(当前位置)  =2 (文件末尾)

.fseek(fp, 100L, 0);

 

面向对象

用typedef 将结构体‘变成’一个类,但其中的函数要用函数指针的形式

typedef struct{

       int num;

       void (*on)();   //函数指针

       void (*off)();

} Light;  //结构体的格式定义了一个

void Light_on(){

       puts("Light_on");

}

void Light_off(){

       puts("Light_off");

}

void Init_Light(int number,Light *obj){ //对象的初始化函数

       obj -> num = number;

       obj -> on = Light_on;

       obj -> off = Light_off;                     //对函数指针进行复制

}

void main(){

       Light m_Light;   //类似于类定义了一个对象

       Init_Light(1, &m_Light); //注意形参为指针

       printf("this is the %dlight\n", m_Light.num);

       (m_Light.on)();

       m_Light.off(); //上两种形式结果相同

}

 

 

编程时使用assert宏,可以给程序调试带来方便

void assert(int expression_r_); //不满足括号内的条件时,会执行assert

assert的作用是现计算表达式 expression_r_ ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。当我们编译代码的时候,如果没有定义NDEBUG这个预编译变量的话,assert就能发生作用。作用就是:如果assert的condition为0的话,abort函数就会被调用,从而导致程序terminate;如果编译的时候定义了NDEBUG,那么,assert宏不会增加任何代码。

在调试结束后,可以通过在包含#include <assert.h>的语句之前插入 #defineNDEBUG 来禁用assert调用,示例代码如下:
#include <stdio.h>
#defineNDEBUG
#include <assert.h>

 

头文件中包含       #include<assert.h>

用法总结与注意事项:
1)在函数开始处检验传入参数的合法性
如:

intresetBufferSize(int nNewSize)
{
//功能:改变缓冲区大小,
//参数:nNewSize 缓冲区新长度
//返回值:缓冲区当前长度
//说明:保持原信息内容不变     nNewSize<=0表示清除缓冲区
assert(nNewSize >= 0);
assert(nNewSize <= MAX_BUFFER_SIZE);

...
}

 2)每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败

不好: assert(nOffset>=0&& nOffset+nSize<=m_nInfomationSize);

好: assert(nOffset >= 0);
assert(nOffset+nSize <= m_nInfomationSize);

 

 

 

 

1. static全局变量与普通的全局变量有什么区别 ?

  全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。

  全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。

  这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。 

  static全局变量只初使化一次,防止在其他文件单元中被引用;   

2.  static局部变量和普通局部变量有什么区别 ?

   把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。  

  static局部变量只被初始化一次,下一次依据上一次结果值;   

3.  static函数与普通函数有什么区别?

   static函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static修饰的函数),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.

static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

 

 

 

 

回调函数:

1、        如排序问题

int sort_function( const void *a, const void *b);

int list[5] = { 54, 21, 11, 67, 22 };

int main(void)

{

int x;

qsort((void *)list, 5, sizeof(list[0]), sort_function); //被调函数

}

 

int sort_function( const void *a, constvoid *b) //回调函数

{

return *(int*)a-*(int*)b;

}

主程序调用函数qsort(被调函数), 而qsort中又调用了主程序提供的sort_function(回调)函数。

回调函数可以象普通函数一样被程序调用,但是只有它被当作参数传递给被调函数时才能称作回调函数。 

2、        一般用户程序调用系统函数,为调用;而系统函数调用用户提供的函数,为回调。【只是一般】

则在用户程序中提供一个回调函数,并注册给系统的一个函数,当系统函数需要进行操作时,则会调用用户提供的回调函数。

使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。

而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。至于如何定义回调函数,跟具体使用的API函数有关,一般在帮助中有说明回调函数的参数和返回值等。C++中一般要求在回调函数前加CALLBACK(相当于FAR PASCAL),这主要是说明该函数的调用方式。

 

3、 模块A有一个函数foo,他向模块B传递foo的地址,然后在B里面发生某种事件(event)时,通过从A里面传递过来的foo的地址调用foo,通知A发生了什么事情,让A作出相应反应。那么我们就把foo称为回调函数。

TRACE

TRACE 宏有点象我们以前在C语言中用的Printf函数,使程序在运行过程中输出一些调试信息,使我们能了解程序的一些状态。但有一点不同的是:

TRACE 宏只有在调试状态下才有所输出,而以前用的Printf函数在任何情况下都有输出。和Printf函数一样,TRACE函数可以接受多个参数如:

int x = 1;

int y = 16;

float z = 32.0;

TRACE( "This is a TRACE statement\n" );

TRACE( "The value of x is %d\n", x );

TRACE( "x = %d and y = %d\n", x, y );

TRACE( "x = %d and y = %x and z = %f\n", x, y, z );

 

要注意的是TRACE宏只对Debug版本的工程产生作用,在Release版本的工程中,TRACE宏将被忽略。

 

 

int* p[2]与 int(*p)[2] 的不同

int *p[2]int* p[2]是一个指向int型的指针数组

p[0]p[1]是两个完全独立的数组,二者的长度可以不同

(int *p)[2] 相当于二维数组,第二维只能为2. Int [len][2].