编程基础知识

来源:互联网 发布:mac新系统 编辑:程序博客网 时间:2024/05/21 13:56

1、枚举

typedef enum {    INFO,    DEBUG,    ERROR,    FATAL,    DEBUG_LAST}**DebugLevel**;  //此处定义了一个枚举类型

然后才能使用
DebugLevel Main_DebugL = FATAL;定义变量。
若换成:

typedef enum DebugLevel{    INFO,    DEBUG,    ERROR,    FATAL,    DEBUG_LAST};  //此处没有定义枚举类型

2、宏定义

可变参数的宏定义
C99中规定宏可以像函数一样带有可变参数,比如

#define LOG(format, ...) fprintf(stdout, format, __VA_ARGS__)

其中,…表示参数可变,VA_ARGS在预处理中为实际的参数集所替换

GCC中同时支持如下的形式

#define LOG(format, args...) fprintf(stdout, format, args)

其用法和上面的基本一致,只是参数符号有变化
有一点需要注意,上述的宏定义不能省略可变参数,尽管你可以传递一个空参数,这里有必要提到”##”连接符号的用法。
“##”的作用是对token进行连接,在上例中,format、VA_ARGS、args即是token,如果token为空,那么不进行连接,所以允许省略可变参数(VA_ARGS和args),对上述变参宏做如下修改

#define LOG(format, ...)     fprintf(stdout, format, ##__VA_ARGS__)#define LOG(format, args...) fprintf(stdout, format, ##args)

上述的变参宏定义不仅能自定义输出格式,而且配合#ifdef #else #endif在输出管理上也很方便,
比如调试时输出调试信息,正式发布时则不输出,可以这样

#ifdef DEBUG#define LOG(format, ...) fprintf(stdout, ">> "format"\n", ##__VA_ARGS__)#else#define LOG(format, ...)#endif

在调试环境下,LOG宏是一个变参输出宏,以自定义的格式输出;在发布环境下,LOG宏是一个空宏,不做任何事情。

http://www.cnblogs.com/morewindows/archive/2011/08/18/2144112.html
宏中的#的功能是将其后面的宏参数进行字符串化操作(Stringizing operator),简单说就是在它引用的宏变量的左右各加上一个双引号。宏中的#@是加单引号(Charizing Operator),宏中的##是拼接字符串。

3、C/C++内存布局、内存分配管理

1、C/C++内存布局:
源文件经过以下几步生成可执行文件:
1、预处理(preprocessor):对#include、#define、#ifdef/#endif、#ifndef/#endif等进行处理
2、编译(compiler):将源码编译为汇编代码
3、汇编(assembler):将汇编代码汇编为目标代码
4、链接(linker):将目标代码链接为可执行文件
讨论C/C++中的内存布局,不得不提的是数据的存储类别!数据在内存中的位置取决于它的存储类别。一个对象是内存的一个位置,解析这个对象依赖于两个属性:存储类别、数据类型。
存储类别决定对象在内存中的生命周期。
数据类型决定对象值的意义,在内存中占多大空间。
C/C++中由(auto、 extern、 register、 static)存储类别和对象声明的上下文决定它的存储类别。
参考:大牛博客:C/C++ Memory Layout
————————————————————————————————————————————————————

#include<stdio.h> int main(void) {      int a=99;      int b=0;      printf("%d,%d \n",&a,&b);      int k=0;      k=(int)&a;      printf("k=%d \n",k);      printf("*k=%d \n",*(int *)k);      printf("*k=%c \n",*(char *)k);      return 0; } 

解释几个问题:
1,* (int * )k 是什么意思? k代表的内存的首地址,为什幺强制转换?根据我们上面的理论,我们要在内存中提出一个值,需要两个条件,一个是首地址,一个是长度。k提供了首地址,那么自然int 就提供了长度。强转是因为,k是int型,而地址是int * 型。也即是,这句话告诉编译器,我们要从k为首地址的地方,提取4个字节的变量值(int),所以*k的值是99
2,(char )k的意思同上,只是这次的长度为1了。所以输出为:(ASSIC码 97)c

#include<stdio.h> int main(void) {     char a[4]={0};    a[0]=1;    a[1]=1;      a[2]=1;      a[3]=1;     printf(" a[0]:%d \n a[1]:%d \n a[2]:%d \n a[3]:%d \n ",&a[0],&a[1],&a[2],&a[3]);     printf("%d\n",*(int *)&a[0]);      return 0;  } 

第二个printf输出为:16843009,它的二进制的形式为1000000010000000100000001
C语言提取内存数据的机制可能是:首先找到变量在内存中的首地址,然后找到他在内存中所占空间的大小,也就是字节数。最后得出该变量的值。具体来说:首先找到a在内存中的首地址(1245052),然后因为a的类型是int所以编译器会从首地址出发找到四个字节的空间,提取出这四个字节中的值作为a的值。
因此,printf(“%d\n”,(int )&a[0]); 此句是取a[0]的地址所指的值,转换为整形指针,是说明空间大小是4字节。

Reference: http://wenku.baidu.com/link?url=TNahNXcyKw1fs1n4zv-1pyu9thCCQTzLyVpRmeOiqUMah38o-B4FyhHzthDC8FyKv_Go8xIzK8kjfhQCI_XOW-q1r8ZZMe4jbs5Ftl6zuEG

2、内存分配管理:
malloc、calloc、realloc的区别:
<1>从静态存储区域分配.
内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量、static变量.
<2>在栈上创建
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限.
<3>从堆上分配,亦称动态内存分配.
程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存.动态内存的生存期由用户决定,使用非常灵活,但问题也最多.
realloc是从堆上分配内存的.当扩大一块内存空间时,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平;如果数据后面的字节不够,问题就出来了,那么就使用堆上第一个有足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上.这句话传递的一个重要的信息就是数据可能被移动.
出自:http://blog.csdn.net/shuaishuai80/article/details/6140979

4、编译预处理

#error token-sequence :在编译的时候输出编译错误信息token-sequence
#undef :取消以前定义的宏定义
#pragma para :设定编译器的状态或者是指示编译器完成一些特定的动作
#pragma message(“消息文本”) 在编译输出窗口中将“消息文本”打印出来
#pragma comment(lib, “comctl32.lib”) 使用lib关键字,连入一个库文件
#pragma resource “.dfm” 把.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义
#pragma once 在头文件的最开始加入,就能够保证头文件只被编译一次
more:http://www.cnblogs.com/qinfengxiaoyue/archive/2012/06/05/2535524.html
http://blog.csdn.net/jx_kingwei/article/details/367312
# line constant “filename”:使得其后的源代码从指定的行号constant重新开始编号,并将当前文件的名命名为filename


5、

6、动态二维数组的内存分配和释放

reference: http://blog.csdn.net/huazhigang/article/details/11745551

7、静态库与动态库

参考:C++静态库与动态库

8、int * p[4]与int (*q)[4]的区别 (前者存储指针,后者存储的整形)

int * p[4] : 表示定义了一个指针数组,即p[0~3]都是一个整形指针,指向一个整数。
    int *c[3];    int d[] = {1,2,0,0};    int e[] = {3,4,5,0};    int f[] = {6,7,8,9};    c[0] = d;    c[1] = e;    c[2] = f;    int j ;    for(i=0;i<3;i++)        for(j=0;j<4;j++)            printf("%d \n",*(*(c+i)+j));

内存模型为:这里写图片描述
int (*q)[4]:
它首先是个指针,即 *q,剩下的“int [4]”作为补充说明,即说明指针q指向一个长度为4的数组。(先将 *q用t表示即:int t[4],定义一个整形数组,)

    int (*a)[5];    int b[5] = {0};    a = (int *)b;    int i;    for(i=0;i<5;i++)    printf("%d  %d\n",*(*a+i),*(b+i));

内存模型为: 这里写图片描述
Ref: http://blog.sina.com.cn/s/blog_715de2f50100ojo9.html

9、字节序(大端小端)的判断、及转换

打印内存信息的实例:

struct Test{    int a;    char b;};void ShowBytes( void * s, int n){    unsigned char * start = (unsigned char * )s;    printf( " [OFFSET] ADDRESS: VALUE\n\n" );    for ( int i = 0 ; i < n; i ++ )    {        printf( " [%.4d]   %.8X:  %.2X\n" , i, start + i, * (start + i));        if ((i + 1 ) % 4 == 0 )        {            printf( "----------------------\n" );        }    } }int main(){    Test *t;    Test a={12,'A'};    t=&a;    ShowBytes(t,8);    return 0;}

这里写图片描述

T:在一个 big endian 的 32 位的计算机上,b 的结果是__

unsigned int a = 0x1234;char b = *((char*)&a);

解析:unsigned int是 4 个字节,0x1234 的完整表示是 0x 00 00 12 34,因为是 big endian,所以,所以 &(取地址) 的话,如果当字节看的话,取到了最左边 00 字节的地址,一定要转换成 char 看的话,值就是 0。

大端——高尾端小端——低尾端
附图:这里写图片描述
Ref: http://www.cnblogs.com/wuyuegb2312/archive/2013/06/08/3126510.html
字节序的概念、判断、及转换

10、常量指针与指向常量的指针

常量指针:
int * const p (指针是常量)
const修饰p,即p本身不能被改变,而其指向的值可以改变。
内存模型:这里写图片描述
指向常量的指针:
const int *p ,const修饰的是int,即p所指向的内容为常量

11、编译链接

REF: http://blog.csdn.net/gzzheyi/article/details/8675495

12、static用法

(1)局部静态变量
静态存储区, 在程序整个运行期间都不释放;只初始化一次;
static局部变量的”记忆性”与生存期的”全局性”
应用:
利用”记忆性”, 记录函数调用的次数(示例程序一)
示例程序一

#include <iostream>using namespace std;void staticLocalVar(){ static int a = 0; // 运行期时初始化一次, 下次再调用时, 不进行初始化工作 cout<<"a="<<a<<endl; ++a;}int main(){ staticLocalVar(); // 第一次调用, 输出a=0 staticLocalVar(); // 第二次调用, 记忆了第一次退出时的值, 输出a=1 return 0;}

(2)外部静态变量/函数
用来表示不能被其它文件访问的全局变量和函数。
此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。

(3)静态数据成员/成员函数
C++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数. 这是与普通成员函数的最大区别, 也是其应用所在, 比如在对某一个类的对象进行计数时, 计数生成多少个类的实例, 就可以用到静态数据成员.

13、指针数组&数组指针

指针数组:array of pointers,即用于存储指针的数组,也就是数组元素都是指针
数组指针:a pointer to an array,即指向数组的指针

    int a[2][4] = {{2,5,6,8},{22,55,66,88}};    int b[4] = {5,8,9,4};    int c[3] = {23,12,43};    int *p[4];    int (*q)[4];    *p = b;    *(p+1) = c;    q = a;    printf("%d\n",**p);    printf("%d\n",*(*(p+1)+1));    printf("%d\n",*(*(q+1)+1));

打印为:5 12 55 说明:p q都是一个二维指针 *(p+1)得到一个一维地址

还要注意的是他们用法的区别,下面举例说明。
int* a[4] 指针数组
表示:数组a中的元素都为int型指针
元素表示:* a[i] * (a[i])是一样的,因为[]优先级高于*
注意:在实际应用中,对于指针数组,我们经常这样使用:

    typedef int* pInt;    pInt a[4];

int (*a)[4] 数组指针
表示:指向数组a的指针
元素表示:(*a)[i]

    int (*a)[10];  //数组指针,指向数组的指针    int i=0;    /*动态开辟空间*/    a=calloc(10,sizeof(int));  //分配了10个int大小的内存    /*赋值*/    for(i=0;i<10;i++)    {        (*a)[i]=i;    }

14、continue/break

    for() 1    {        for() 2        {            if()              break;/continue;            A;        }        B;    }

break: 跳出当前循环,不再判断当前循环的条件。继续执行B,完后执行1处的for循环。
continue: 跳出当前循环,再判断当前循环的条件。继续执行2处的for循环。

struct & typedef struct

1 首先://注意在C和C++里不同
在C中定义一个结构体类型要用typedef:

typedef struct Student{    int a;}Stu;

声明变量的时候就可:Stu stu1;
如果没有typedef就必须用:struct Student stu1;
这里的Stu实际上就是struct Student的别名。Stu==struct Student

在C++中定义一个结构体类型不需要typedef:

struct Student{    int a;}; 

声明变量时直接:Student stu2;

2、在c++中如果用typedef的话,又会造成区别:

struct   Student   {       int   a;   }stu1;    //stu1是一个变量 //使用时可以直接访问stu1.a typedef   struct   Student2   {       int   a;   }stu2;   //stu2是一个结构体类型=struct Student  //stu2则必须先   stu2 s2;//然后           s2.a=10;

3、

typedef struct tagMyStruct{      int iNum;     long lLength;} MyStruct;

tagMyStruct是标识符,MyStruct是变量类型(相当于(int,char等))

判断奇偶(使用与运算)

bool isEven(int n){    return (n & 1) == 0;}
0 0