1
a=*p++
解释如下:
*p++=*(P++) 等价于 a=*p P++
优先级++高于*
#include "stdafx.h"
#include "stdio.h"
void main(){
int a[5]={100,2,3,4,5};
int *p = a;
int b=0,c=0;
b=*(p++);
c=*p;
printf("%d %d",b,c);
getchar();
getchar();
getchar();}
2
原码
就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
反码
的表示方法是:
正数的反码是其本身
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
补码
的表示方法是:
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
3
全局变量没初始化(静态) 默认为0
非静态局部变量 没初始化 则这个值 随机的
4
extern关键字
也就是说extern有两个作用,
第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非
第二,当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块活其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。
extern(叫声明 不叫定义)
在源文件A里定义的函数,在其它源文件里是看不见的(即不能访问)。为了在源文件B里能调用这个函数,应该在B的头部加上一个外部声明:
extern 函数原型;
这样,在源文件B里也可以调用那个函数了
举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。能够被其他模块以extern修饰符引用到的变量通常是全局变量
5
A
int (*a)(int)
一个指向函数的指针 该函数有一个整形参数 并返回一个整形数
int (*a[10])(int)
一个有10个指针的数组 该指针指向一个函数 该函数有一个整形数并返回一个整形数
B
(*a)[10]数组指针 和 *a[10] 指针数组
6
编程技巧、
while(i=0)循环结束
while(i=1) 死循环
int a=5 b=4 c=3
\a>b>c 的值为0 先a>b
int a=(b=(c=020)&&(1==2)) 先c=020在&&0 所以b=0 a=0 等号(=)的优先级最低
7
printf 语句
#include <stdio.h>
void main()
{
int a=5,b=2;
printf("%d %d\n",b=a+1,a=a+1);
}
输出结果是:7 6
而不是:6 6
这是因为printf函数的计算是从右向左进行的。
8 短路求值
(条件1&&条件2) 如果条件1满足 条件2的语句不执行
9
char *c1 = "abc";实际上先是在文字常量区分配了一块内存放"abc",然后在栈上分配一地址给c1并指向
这块地址,然后改变常量"abc"自然会崩溃
然而char c2[] = "abc",实际上abc分配内存的地方和上者并不一样,可以从
char c[]="hello word" 是分配一个局部数组
char* c="hello word" 是分配一个全局数组、
因为这个函数返回的是一个局部变量地址,当调用这个函数后,这个局部变量str 就释放了 所以返回的结果是不安全的 不稳定的 随时度有可能被收回的可能
10
int *ptr;
ptr=(int *)0x8000;//
*ptr=3;
解答:指针问题,首先将指针ptr指向一个指定的地址,即对一个未作声明的地址直接进行访问,所以访问出错。这块地址可能已经被用,或者被其他程序占用等,因此会导致错误。
在一个不确定的地址上面进行直接访问,导致出错
数组名本身就是指针 再加& 就变双指针了 这里的双指针就变成二维数组 加1 就是整体加一行
数组做形参时候 就退化为指针了
11
逗号表达式
(表达式1,表达式2,表达式3)计算从左往右 整体结果为最后一个表达式的结果
12 预编译
在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时,就会出现大量重定义的错误。在头文件中实用#ifndef #define #endif能避免头文件的重定义。
13 extern 关键字用法
例如文件a.c需要引用b.c中的变量int v,就可以在a.c中申明extern int v;这样就可以使用
a.h b.c c.c 其中a.h分别被包含在 b.c和c.c中 如果要定义一个在b c都能使用的
变量 若在a.h中定义是不行的 只能在其中一个C文件中定义 另外一个用extern引用
14 全局变量 静态全局变量 静态局部变量 局部变量 总结
A 全局变量
(a)若程序由一个源文件构成时,全局变量与全局静态变量没有区别。
(b)若程序由多个源文件构成时,全局变量与全局静态变量不同:全局静态变量使得该变量成为定义该变量的源文件所独享,即:全局静态变量对组成该程序的其它源文件是无效的,那么作用域就在本文件,其他.c或.h文件要用到此变量,用extern 也是不行的。
(c)具有外部链接的静态;可以在所有源文件里调用;除了本文件,其他文件可以通过extern的方式引用;
B 静态全局变量的作用:
(a)不必担心其它源文件使用相同变量名,彼此相互独立。
(b)在某源文件中定义的静态全局变量不能被其他源文件使用或修改。
(c) 只能在本文件中使用!具有内部链接的静态;不允许在其他文件里调用;
C 静态局部变量
静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见
D 局部变量
局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
E 总结:
全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间
15 不用sizeof来判断系统的位数
//无符号,若为16微系统 则0-65535 对0取反后111111111111111为65535
若为32 1111111111111111111111111111111肯定大于65536
void main()
{
unsigned int a=~0;
if(a>65536)
printf("32");
else
printf("16");
getchar();
getchar();
}
// 在32位系统下 输出65536 65535
// 在16位系统下 输出 0 -1
void main()
{
unsigned int i=65536;
unsigned int j=65535;
//在内存中存补码 1111111111111111 减1的11.。。。10 取反100---01(源码) 所以打印-1
printf("%d\n",j);
printf("32系统");
getchar();
getchar();
}
%u 无符号输出 %d 有符号输出、
i*-1代表-i
16 内存分配方式
[1]从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
[2]在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
[3]从堆上分配,亦称动态内存分配。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
17 C++虚拟函数表
博客地址;http://blog.csdn.net/haoel/article/details/1948051/
A 虚函数基础
typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
// Invoke the first virtual function
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
实际运行经果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)
虚函数表地址:0012FED4
虚函数表 — 第一个函数地址:0044F148
Base::f
B 虚函数进阶(已经过编译成功)
#include "stdafx.h"
#include <iostream>
using namespace std;
class Base1 {
public:
virtual void f() { cout << "Base1::f" << endl; }
virtual void g() { cout << "Base1::g" << endl; }
virtual void h() { cout << "Base1::h" << endl; }
};
class Base2 {
public:
virtual void f() { cout << "Base2::f" << endl; }
virtual void g() { cout << "Base2::g" << endl; }
virtual void h() { cout << "Base2::h" << endl; }
};
class Base3 {
public:
virtual void f() { cout << "Base3::f" << endl; }
virtual void g() { cout << "Base3::g" << endl; }
virtual void h() { cout << "Base3::h" << endl; }
};
class Derive : public Base1, public Base2, public Base3 {
public:
virtual void f() { cout << "Derive::f" << endl; }
virtual void g1() { cout << "Derive::g1" << endl; }
};
typedef void(*Fun)(void);
int main()
{
Fun pFun = NULL;
Derive d;
int** pVtab = (int**)&d;
//Base1's vtable
//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0);
pFun = (Fun)pVtab[0][0];
pFun();
//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);
pFun = (Fun)pVtab[0][1];
pFun();
//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);
pFun = (Fun)pVtab[0][2];
pFun();
//Derive's vtable
//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);
pFun = (Fun)pVtab[0][3];
pFun();
//The tail of the vtable
pFun = (Fun)pVtab[0][4];
cout<<pFun<<endl;
//Base2's vtable
//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);
pFun = (Fun)pVtab[1][0];
pFun();
//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);
pFun = (Fun)pVtab[1][1];
pFun();
pFun = (Fun)pVtab[1][2];
pFun();
//The tail of the vtable
pFun = (Fun)pVtab[1][3];
cout<<pFun<<endl;
//Base3's vtable
//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);
pFun = (Fun)pVtab[2][0];
pFun();
//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);
pFun = (Fun)pVtab[2][1];
pFun();
pFun = (Fun)pVtab[2][2];
pFun();
//The tail of the vtable
pFun = (Fun)pVtab[2][3];
cout<<pFun<<endl;
getchar();
getchar();
return 0;
}
构造函数不能为虚函数?
构造函数不能为虚函数的原因如下:
从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。
析构函数可以为虚函数 而且通常申明为虚函数 ?是的
博客地址 http://www.cnblogs.com/lixiaohui-ambition/archive/2012/07/13/2589716.html
在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。
内联函数不能为虚函数?是的
如果函数已经被声明为inline, 内联函数已经在编译期间它的调用点上就被展开;而虚拟函数调用的决定则要等到运行时刻在执行程序内部的每个调用点上系统根据被调用对象的实际基类或派生类的类型来决定选择哪一个虚拟函数实例,因此虚拟函数在编译期间并不能被展开,所以内联函数不能成为虚拟函数.
静态成员函数不能为虚函数? 是的
虚函数是为了多态设计的,静态成员函数独立于对象存在,没有this指针,所以不能设计成虚函数,而类的普通成员函数(包括虚函数)在编译时加入this指针,通过这种方式可以与对象捆绑,而静态函数编译时不加this,因为静态函数是给所有类对象公用的,所以没有在编译时加this,所以无法与对象捆绑,而虚函数就是靠着与对象捆绑加上虚函数列表才实现了动态捆绑。所以没有this指针虚函数无从谈起。
友元函数
对于没有继承特性的函数没有虚函数的说法
18
float与0值的比较
无论是float还是double 他们在内存中存储机制和整形数不同 有舍入差、
浮点运算可交换,但是不满足结合律
#include "stdafx.h"
#include<iostream>
using namespace std;
void main(int argc, char* argv[])
{
float x=0.000009;
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON))
printf("x=0\n");
else printf("x!=0\n");
getchar();
getchar();
}
19
A
char string[10];
char* str1="0123456789";
strcpy(string,str1);
str1里面有11个字符(外加\0)
而string[10]只有10个字符
所以出现越界情况
B
char * str1
char string[10]
if(strlen(str1)<=10)
{
strcpy(string,str1);
}
由于strlen不计算\0 所以应改为strlen(str1)<10
19
const 用法
主要作用定义数据为常类型
一、指向常量的指针
定义: const 数据类型 *指针变量名
含义: 一个指向常量的指针变量。
说明: 此指针可更改指向其他的常量,但此指针所指向的常量不能被更改。
例子
const char *name = "chen"; //声明指向常量的指针name
name[3] = 'a'; //出错,指针所指向的变量的值不能改变
name = "zhang"; //合法,指针自身的值可改变
(另一种表达式 char const *name)
二 常指针
定义: 数据类型 *const 指针变量名 = 初始化值
含义: 一个指向变量的不可改变的指针变量。
说明: 在定义常指针时必须初始化;
此指针的值不可改变,但此指针所指向的变量值可改变。
例子:
char *const name = "chen"; //声明常指针name
name[3] = 'a'; //合法,指针所指向的变量值可改变
name = "zhang"; //出错,指针自身值不可改变
三 指向常量的长指针
定义: const 数据类型 *const 指针变量名 = 初始化值
含义: 一个指向常量的常指针变量。
说明: 在定义时此指针必须初始化;
此指针的值和它所指向的常量的值均不可被改变。
例子:
const char *const name = "chen"; //声明指向常量的常指针
name[3] = 'a'; //出错,指针所指向的变量值不可改变
name = "zhang"; //出错,指针自身值不可改变
还有就是 const int a=3和int const=3意思完全一样
常引用:
常引用也称const引用,之所以引入常引用,是为了避免在使用变量引用时,在毫不知情的情况下改变了变量的值,从而引起程序错误
const 类型标示符 & 引用名 = 目标变量
断言assert()捕获非法情况 括号里的表达式为真 则继续执行 为假就停止
#define TRACE(S) (printf("%s\n",#S),S) 解释如下
首先 printf("%s\n",#S)代表 printf("%s\n","S")
然后 TRACE(S)这个表达式去S值 这是个逗号表达式 所以取最后的值
20
野指针的避免方法
指针变量没有被初始化时候,解决办法,声明时候初始化,
可以是具体指值,也可以让他指向NULL,
指针P被free()或者delete()之后,没有设置为NULL。解决办法:
指针指向的内存空间被释放之后,指针应该指向NULL
21
综合性问题
char *c1 = "abc";实际上先是在文字常量区分配了一块内存放"abc",然后在栈上分配一地址给c1并指向
这块地址,然后改变常量"abc"自然会崩溃
然而char c2[] = "abc",实际上abc分配内存的地方和上者并不一样,可以从
char c[]="hello word" 是分配一个局部数组
char* c="hello word" 是分配一个全局数组、
因为这个函数返回的是一个局部变量地址,当调用这个函数后,这个局部变量str 就释放了 所以返回的结果是不安全的 不稳定的 随时度有可能被收回的可能
int *ptr;
ptr=(int *)0x8000;
*ptr=3;
解答:指针问题,首先将指针ptr指向一个指定的地址,即对一个未作声明的地址直接进行访问,所以访问出错。这块地址可能已经被用,或者被其他程序占用等,因此会导致错误。
在一个不确定的地址上面进行直接访问,导致出错
数组名本身就是指针 再加& 就变双指针了 这里的双指针就变成二维数组 加1 就是整体加一行
数组指针和指针数组的区别
数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
指针数组
数组做形参时候 就退化为指针了
当派生类中新增加的数据或函数与基类中原有的同名时候 若不加限制 则优先
调用派生类中的成员
基类指针只能从派生类中继承子类部分
22
A
友元函数性质:
关键字 friend开头
友元函数不是成员函数 使用时候不要:: 成员函数使用才要这个符号
友元函数不能直接访问类的成员 只能访问对象成员
友元函数可以访问对象私有成员 但是普通函数不行
调用友元函数时候 在实际参数中指明要访问的对象
类与类之间的友元关系不能继承 成员函数才要继承之说
B
拷贝构造函数
该函数名与类同名 也是一种构造函数
该函数只有一个参数 并且是某个对象的引用
每个类必须有一个拷贝构造函数
用户没定义 系统会自动生成一个缺省拷贝构造函数
拷贝构造函数的目的是创建一个新的对象实体 一定要争新的对象有自己的空间
而不是与先前对象共用
CA(const CA& C)就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(X& x)。
当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
深拷贝和潜拷贝
如果一个类拥有资源,当这个类在发生复制过程中 资源重新分配 称为深拷贝
反之对象存在资源 但在复制过程并没复制资源的情况为潜拷贝
基类的构造函数和析构函数度不能被派生类继承
类的成员变量的初始化顺序只与变量在类中申明的顺序有关 与在构造函数中初始化顺序
无关。
23
数组和指针的区别
A 修改内容
字符数组a的容量是6个字符,其内容为hello\0。a的内容可以改变,如a[0]= ‘X’。指针p指向常量字符串“world”(位于静态存储区,内容为world\0),常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句p[0]= ‘X’有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。
char a[] = “hello”;
a[0] = ‘X’;
cout << a << endl;
char *p = “world”; // 注意p指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误 运行错误
cout << p << endl;
B
用运算符sizeof可以计算出数组的容量(字节数)。示例3(a)中,sizeof(a)的值是12(注意别忘了’\0’)。指针p指向a,但是sizeof(p)的值却是4。这是因为sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char*),而不是p所指的内存容量。C++/C语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。
C 注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针
24
请说出const与#define 相比,有何优点?
A const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
B 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
25
堆与栈的区别
堆和栈的区别
一、预备知识—程序的内存分配
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"
优化成一个地方。
}
二、堆和栈的理论知识
申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空
间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = new char[10];
但是注意p1、p2本身是在栈中的。
申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢
出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表
中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的
首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部
分重新放入空闲链表中。
申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意
思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有
的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将
提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储
的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小
受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是
直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。
堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可
执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈
的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地
址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
存取效率的比较
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到
edx中,再根据edx读取字符,显然慢了。
小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就
走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自
由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由
度大。
26
A
一个参数既可以是const还可以是volatile吗?解释为什么。
解:是的。比如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
B
小段模式和大端模式
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
#include <stdio.h>
//判断操作系统是大端还是小端
bool checkendian()
{
union my
{
int a;
char b;
};
my t;
t.a=1;
return(t.b==1);
}
int main()
{
printf("%d\n",checkendian());
return 0;
}
C
下面的代码片段的输出是什么,为什么?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
答案输出:Got a valid pointer
解释:当使用malloc后,只有在没有足够内存的情况下会返回NULL,或是出现异常报告。
malloc(0),系统就已经帮你准备好了堆中的使用起始地址(不会为NULL)。但是你不能对该地址进行写操作(不是不允许),如果写了话,当调用free(ptr)就会产生异常报告(地址受损)
NULL一般预定义为(void *)0,指向0地址。malloc是在程序堆栈上分配空间,不会是0地址 malloc(0)是指分配内存大小为零 NULL是不指向任何实体 malloc(0)也是一种存在不是NULL
27
void main(char (&p)[10])
{
printf("%d\n",sizeof(p));
getchar();
getchar();
}
打印 10
不要在类的构造或者析构过程中调用虚函数,因为这样的调用永远不会沿类继承树往下传递到子类中去。
int main(void) // :X86_64环境
{
int a[4][4] = {
{1,2,3,4},
{50,60,70,80},
{900,1000,1100,1200},
{13000,14000,15000,16000} };
int (*p1)[4] = a;
int (*p2)[4] = &a[0];
int *p3 = &a[0][0];
printf("%d %d %d %d\n",*(*(a+1)-1),*(*(p1+3)-2)+1,*(*(p2-1)+16)+2,*(p3+sizeof(p1)-3));
return 0;
}
解释::
4 1101 13002 60
p1为指向一维数组的指针,所以a + 1指向{50,60,70,80}这一维的地址。减一则为4的地址;同理第二个输出1101。同理,由于数组的列是4,所以*(p2 - 1) + 16就相当于*(p2) + 12,所以第三个输出13002。
第四个由于p1是指针,所以sizeof(p1)为8(68位的系统),所以第四个输出60。
typedef struct object object;
struct object
{
char data[3];
};
int main(void)
{
object obj_array[3] = { {'a','b','c'},
{'d','e','f'},
{'g','h','i'} };
object* cur = obj_array;
printf("%c %c\n", *(char*)((char *)(cur)+2) , *(char*)(cur+2));
return 0;
}
答案:c g
int a[2][3]={1,2,3,4,5,6};
printf("%d\n",*(*(a+1)+1)); 5
printf("%d\n",**(a+1)); 4
printf("%d\n",*(*a+1)); 2
class A
{
public:
int b;
char c;
virtual void print()
{
cout<<"this is father's function!"<<endl;
}
};
class B : A
{
public:
virtual void print()
{
cout<<"this is children's function!"<<endl;
}
};
int main(void)
{
cout<<sizeof(A)<<" "<<sizeof(A)<<endl;
return 0;
}
输出 12 12 (数据按四字节对其)
32位系统
struct st
{
char ch , *ptr;
union
{
short a , b;
unsigned int c : 2 , d : 1;
};
bool f;
struct st *next;
};
大小20字节
采用new操作符创建对象时,如果没有足够内存空间而导致创建失败,则new操作符会返回NULL?
错 new在失败后抛出标准异常std::bad_alloc而不是返回NULL。
28
在Intel CPU上,以下多线程对int型变量x的操作,哪几个不是原子操作,假定变量的地址都是对齐的?(ABC)
A、x = y(两句) B、x++(三句) C、++x(三句) D、x = 1(一句)
一般情况下,下面哪些操作会执行失败?(BCD)
class A
{
public:
string a;
void f1()
{
printf("Hello World");
}
void f2()
{
a = "Hello World";
printf("%s",a.c_str());
}
virtual void f3()
{
printf("Hello World");
}
virtual void f4()
{
a = "Hello World";
printf("%s",a.c_str());
}
};
A、A *aptr = NULL; aptr->f1();
B、A *aptr = NULL; aptr->f2();
C、A *aptr = NULL; aptr->f3();
D、A *aptr = NULL; aptr->f4();
至于A为什么正确,因为A没有使用任何成员变量,而成员函数是不属于对象的,所以A正确。其实,A* aptr = NULL;aptr->f5();也是正确的,因为静态成员也是不属于任何对象的。至于BCD,在B中使用了成员变量,而成员变量只能存在于对象,C有虚表指针,所以也只存在于对象中。D就更是一样了。但是,如果在Class A中没有写public,那么就全都是private,以至于所有的选项都将会失败。(根本没有定义对象)
如果改成 A bbb;
A *aptr = &bbb;
aptr->f1(); 正确
aptr->f2(); 正确
aptr->f3(); 正确
aptr->f4(); 正确
已知*p=NULL, *q=new char[100],sizeof(p) 和 sizeof(q)的值各为多少?
答案 4和4
29
A
char* GetMem()
{
char p[] = "hello";
return p;
}
void test_get_mem()
{
char *p = GetMem();
printf(p);
return ;
}
GetMem函数中的p是一个在栈上的局部变量,当函数运行结束的时候,栈上的内容会自动释放的,此处返回的值有可能会成为一个野指针,会出现一个意想不到的结果。
B
void test_mem()
{
char *p = new char[64];
delete p;
p = NULL;
return ;
}
应该修改为 delete[]p; p指向的是一个字符型的数组空间,原来的代码只是简单的释放了指向申请空间的指针,并没有释放申请的空间,容易造成内存崩溃。
回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。
C
class A
{
}; 1
class B
{
public:
B();
virtual ~B();
}; 4 地址指针占了4个字节
class B
{
public:
B();
}; 1
class C
{
private:
#pragma pack(4)
int i;
short j;
float k;
char l[64];
long m;
char *p;
#pragma pack()
};
class D
{
private:
#pragma pack(1)
int i; //4
short j; //2
float k; //4
char l[64]; //64
long m; //4
char *p; //4 double 8
#pragma pack()
};
int main(void)
{
printf("%d\n",sizeof(A));
printf("%d\n",sizeof(B));
printf("%d\n",sizeof(C));
printf("%d\n",sizeof(D));
getchar();
getchar();
return 0;
}
答案 1、4、84、82
以下while循环执行(C)次。(考数的存储 补码形势)
unsigned int k = 20;
while(k >= 0)
--k;
A、20次 B、一次也不执行 C、死循环 D、21次
如果改成 int k=20;
则执行21次
int a=5,则 ++(a++)的值是(D)
A、5 B、 6 C、7 D、逻辑错误
a++返回的是一个临时变量,这里是右值,不能再前面++了
在32位系统中,char str[]="xuelei"; char *p = str; sizeof(str)=() ,sizeof(p)=() ,sizeof(*p)=()
答案分别是: 7、4、1,分别对数组、指针和一个字符类型求大小。。
char a = 255;
printf("%d\n",a);
由于255的二进制表示是1111 1111,将其作为int类型输出的时候,由于最高位是1,表示的是一个负数,其表示的数字就是将最高位后面的7个1取反后在加上1,表示的就是-1,所以第二个输出应该是-1。
若调用一个函数,且此函数中无return语句,则正确的说法是
返回一个不确定的值
30
A
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"A"<<endl;
}
};
int main(void)
{
A a[4], b,*p;
}
输出5个A
p只是一个对象指针,并没有指向一个对象的内存空间,所以没有调用构造函数。
B
如下的字符串函数,用于生存一个字符串 ”连接号码异常” ,并返回它的指针
[cpp] view plaincopy
char* strfun()
{
char str[20];
strcpy(str, “连接号码异常”);
printf(“%s \n”, str); //printf语句1
return str;
}
void main()
{
char *pstr = strfun();
printf("%s \n", pstr); //printf语句2
}
问题1 : printf语句1和printf语句2哪个能在屏幕上正在打印出来?
问题2 : 如果不能正常在屏幕上打印出字符串,请说明原因。
问题3 : 如果不修改strfun的声明,请问该如何修改上述程序的错误。
答:
问题1:语句1可以正常打印,语句2不能正常打印;
问题2:语句2使用的指针所指向的内存空间str[20],在函数strfun返回时已经被释放了;
问题3:可以将函数strfun中的语句char str[20];改为char *str = new char[20];
long不能做switch表达式的值 汉子存储占两个字节
给出以下定义, 则正确的叙述为
char x[]="abcdefg";
char y[]={'a','b','c','d','e','f','g'};
A、数组X和数组Y等价
B、数组X和数组Y长度相同
C、数组X的长度大于数组Y的长度(正确)
D、数组X的长度小于数组Y的长度
printf("%d\n",sizeof(x)); 输出8
printf("%d\n",sizeof(y)); 输出7
因为x数组是字符串数组,后面还有结束符:“\0”,所以长度为:8
而y数组就是普通的字符数组,没有“\0”结束符的,所以长度为:7
定义数组
define N 10;
int x[N]; 正确
int N = 10; //这个是错误的,数组的大小应该是一个常量表达式
int x[N];
const int N = 10; //这个是正确的
int x[N];
31
A
int a=5,则 ++(a++)的值是(逻辑错误)
A、5 B、 6 C、7 D、逻辑错误
a++返回的是一个临时变量,这里是右值,不能再前面++了
B
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char* str5 = "abc";
const char* str6 = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 输出 F
cout << boolalpha << ( str3==str4 ) << endl; // 输出 F
cout << boolalpha << ( str5==str6 ) << endl; // 输出 T
答:
分别输出false,false,true。
str1和str2都是字符数组,每个都有其自己的存储区,它们的值则是各存储区首地址,不等;
str3和str4同上,只是按const语义,它们所指向的数据区不能修改。
str5和str6并非数组而是字符指针,并不分配存储区,其后的“abc”以常量形式存于静态数据区,而它们自己仅是指向该区首地址的指针,所以相等。
C
unsigned int const size1 = 2;
char str1[ size1 ];//正确
unsigned int temp = 0;
cin >> temp;
unsigned int const size2 = temp;
char str2[ size2 ]; //错误
str2定义出错,size2非编译器期间常量,而数组定义要求长度必须为编译期常量。
cout<< (true?1:"0") <<endl;
答:三元表达式“?:”问号后面的两个操作数必须为同一类型
struct Test
{
Test(int ) { }
Test() { }
void fun() { }
};
int main(void)
{
Test a(1);
a.fun();
Test b(); //变量b定义出错。按默认构造函数定义对象,不需要加括号。
b.fun();
return 0;
}
char *GetMemory(void)
{
char *p = "Hello World";
return p; // 可以返回 地址在堆上
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
strcpy(str,"abc"); //无法修改字符串常量的值
printf("%s",str);
}
有问题,GetMemory函数中的p指针指向的是一个字符串常量,返回后,str也是指向这个字符串常量,是无法修改字符串常量的值。
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str,"Hello World");
printf("%s",str);
}
没有问题,完全正确,因为指针作为函数参数进行传递时,只能改变指针指向的值,而不能改变指针的指向,如果想要改变指针的指向,就要使用二级指针该操作。
void GetMemory(char *p)
{
p=(char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str,"Hello World");
printf("%s",str);
}
有问题,Test函数中str一直都是NULL,并没有为其分配空间,所以无法将字符串拷贝到str指向的内存空间。GetMemory函数中的p指针只是一个临时变量,并不是str指针,并没有修改str指针的指向。(指针的传递只能改变指针指向的值,要改变指针的指向只能用双重指针)
void *pp = malloc(10);
cout << sizeof(pp) << endl; //答案 4
char pString2[100] = "google";
int size3 = sizeof(pString2);//答案100
int size3 = strlen(pString2);//答案6
32
A
1 #include<stdio.h>
2
3 int main(void)
4 {
5 int a = 10, b = 20, c = 30;
6
7 printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2));
8
9 return 0;
10 }
复制代码
A:输出的结果为
110..40..60
函数的参数是从右到左执行的,但是打印是从左到右的
char buff[10];
memset(buff,0,sizeof(buff));
gets(buff);
问题在于gets()函数,这个函数是接收标准输入的一串字符串,并且没有检查字符串缓冲区的大小就
直接拷贝到buff数组中,这可能导致在写入buff内存时溢出,可以使用fgets()函数代替这个函数,
main()
{
char *p1=“name”;
char *p2;
p2=(char*)malloc(20);
memset (p2, 0, 20);
while(*p2++ = *p1++); (地址到空 字符串末端)
printf(“%s”,p2);
}
Answer:empty string. 打印为空
栈:由编译器自动分配、释放。在函数体中定义的变量通常在栈上。
堆:一般由程序员分配释放。用new、malloc等分配内存函数分配得到的就是在堆上。
class A
{
public:
A() { p=this; }
~A() { if(p!=NULL) { delete p; p=NULL; } }
};
A* p;
答: delete 会自动调用析构函数。所以析构中调用 delete 引起了无限递归。
Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:
#define dpS struct s *
typedef struct s * tpS;
以上两种情况的意图都是要定义dpS 和 tpS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么?
这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。
思考下面的例子:
dpS p1,p2;
tpS p3,p4;
第一个扩展为
struct s * p1, p2;
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。
int p = 1234;
printf("%2d\n",p); 输出1234 %1d %2d %3d %4d 都是输出1234
int a=1234; printf("%5d\n",a);
那么输出应该是:
_1234 【这里_表示一个空格】
如果是("%50d\n",a),那么1234之前就有50-4=46个空格。
这样明白了吧
33
左值和右值的理解
A(左边要确切 不能出现临时变量)
个人感觉,左值就是可以提供一个确切的可操作的地址,比如前两个赋值语句,左值都是正确的,因为它提供了一个确定的地址,
变量名其实是一个特殊的对地址的引用。第三条赋值语句错误,左值不正确,计算机计算a+24时会将结果存在某个地址,但是这对于我们是未知的,
正如一个普通的整型常量是不可以作为左值的。那是一个不合法的地址.
int a;
int * p ;
a = 29; //true
*p = 30 ; //true
( a + 24) = 10 ; //false
B
C/C++语言中可以放在赋值符号左边的变量,即具有对应的可以由用户访问的存储单元,并且能够由用户去改变其值的量。左值表示存储在计算机内存的对象,
而不是常量或计算的结果。
或者说左值是代表一个内存地址值,并且通过这个内存地址,就可以对内存进行读并且写(主要是能写)操作;这也就是为什么左值可以被赋值的原因了。相对应的还有右值:当一个符号或者常量放在操作符右边的时候,计算机就读取他们的“右值”,也就是其代表的真实值。简单来说就是,左值相当于地址值,右值相当于数据值。右值指的是引用了一个存储在某个内存地址里的数据。
3 = a;//不合法。
a+b = 4;//不合法。
若函数fun的函数头为:
int fun(int i , int j)
且函数指针变量p指向函数fun的赋值语句是 P=fun;
(以下待定)
(void *)ptr 和 (*(void**))ptr的结果是否相同?其中ptr为同一个指针
(void *)ptr 和 (*(void**))ptr值是相同的
这不是指针函数 这是强制类型转化
第一个没问题 就是把ptr强制转化为指向空类型的指针
第二个(*(void**))ptr
(void**)ptr 先看里面的 这是转化成指向void指针的指针 也就是二级指针
再在前面加上*就是取内容 那么内容也是个指针
这个指针是指向空类型的 所以强制转化后和第一个是一样的
34
A
假定要对类AB定义加号操作符重载成员函数,实现两个AB类对象的加法,并返回相加结果,则该成员函数的声明语句为:(??B??)注意 这里是成员函数
?
A.?AB?operator+(AB?&?a?,?AB?&?b)???
????
B.AB?operator+(AB?&?a)?
C.operator+(AB?a)?????
????????????????
D.AB?&?operator+(?)
如果是友元函数 就选A
B
int?square(volatile?int?*ptr)?
?{?
????????return?*ptr?*?*ptr;?
?}?
?
这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
?int?square(volatile?int?*ptr)?
?{?
????int?a,b;?
????
? a?=?*ptr;?
?????
b?=?*ptr;?
???
?return?a?*?b;?
?
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
?long?squart(volatile?int?*ptr)
??{???
??int?a;???
??a?=?*ptr;????
??return?a?*?a;?
?}
要对绝对地址0×100000赋值,我们可以用 (unsigned int*)0×100000 = 1234; 那么要是想让程序跳转到绝对地址是0×100000去执行,应该怎么做?
答案:*((void (*)( ))0×100000 ) ( );
首先要将0×100000强制转换成函数指针,
即: (void (*)())0×100000 然后再调用它: *((vovd (*)())0×100000)();
用typedef可以看得更直观些: typedef void(*)() voidFuncPtr; *((voidFuncPtr)0×100000)();
对齐方式
使用伪指令#pragma pack (n),编译器将按照n 个字节对齐;
使用伪指令#pragma pack (),取消自定义字节对齐方式。
注意:如果#pragma pack (n)中指定的n 大于结构体中最大成员的size,则其不起作用,结构体仍然按照size 最大的成员进行对界。
另外,通过__attribute((aligned (n)))也可以让所作用的结构体成员对齐在n 字节边界上
野指针”不是NULL指针,是指向“垃圾”内存(不可用内存)的指针。“野指针”是很危险的,if无法判断一个指针是正常指针还是“野指针”。有个良好的编程习惯是避免“野指针”的唯一方法。
野指针的成因主要有三种:
一、指针变量没有被初始化。指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
三、指针操作超越了变量的作用范围。比如不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。比如说某个地址的生命期,使用一个没有生命期的指针是非常危险的。
35
A
虚成员函数不可能是static成员函数
struct MyStruct
{
char dda;
double dda1;
int type;
};
printf("%d\n",sizeof(MyStruct)); 打印出来24
(即结构中占用最大空间的类型所占用的字节数sizeof
(double)=8)的倍数
int *a[2][3];
printf("%d\n",sizeof(a)); 答案 24 (64位系统和32为系统)(有待商榷)
简述Critical Section和Mutex的不同点
答: 对几种同步对象的总结
1.Critical Section A.速度快 B.不能用于不同进程
C.不能进行资源统计(每次只可以有一个线程对共享资源进行存取)
2.Mutex A.速度慢 B.可用于不同进程 C.不能进行资源统计
3.Semaphore A.速度慢 B.可用于不同进程 C.可进行资源统计(可以让一个或超过一个线程对共享资源进行存取)
4.Event A.速度慢 B.可用于不同进程 C.可进行资源统计
realloc函数在使用上要注意什么问题。 这个函数我也才知道的,汗一个。据我的初步理解,这个函数的作用是重新分配空间大小,返回的头指针不变,只是改变空间大小。既然是改变,就有变大、变小和为什么改变的问题。变大,要注意不能大到内存溢出;变小,那变小的那部分空间会被征用,原有数据不再存在;为什么改变,如果是想重新挪作他用,还是先free了吧。
strtok函数在使用上要注意什么问题。 这个问题我不知道能不能回答全面,因为实在是用的很少。这个函数的作用是分割字符串,但是要分割的字符串不能是常量,这是要注意的。比如先定义一个字符串:char array[]="part1,part2";,strtok的原形是char *strtok(char *string, char *delim);,我们将","作为分隔符,先用pt=strtok(array,",");,得到的结果print出来就是"part1",那后面的呢,要写成pt=strtok(NULL,",");,注意,要用NULL,如果被分割的字符串会被分成N段,那从第二次开始就一直要用NULL。总结起来,需要注意的是:被分割的字符串和分隔符都要使用变量;除第一次使用指向字符串的指针外,之后的都要使用NULL;注意使用这个函数的时候千万别把指针跟丢了,不然就全乱了。
int a=2,b=11,c=a+b++/a++;
printf("%d\n",c);答案 7
等价于 c=2+11/2 a++ b++
局部变量能否和全局变量重名?
能,局部会屏蔽全局。要用全局变量,需要使用”::”
多线程和单线程各自分别在什么时候效率更高?
多线程在并发,并且各线程无需访问共享数据情况详细最高
如果多线程过于频繁切换,或共享数据很多情况下,使用单线程较好
三维数组
多个相同的二维数组可以用三维数组表示。或者说三维数组是以二维数组为元素的数组。
比如一个城市有10个学校,每个学校有10个班,每个班有40个学生,那么表示这10个学校的学生的语文成绩的时候,就可以用一个三维数据 a[10][10][40] 来存储。
#include <stdio.h>
int main(void)
{
int row, column, table;
float values[2][3][5] = {
{{1.0, 2.0, 3.0, 4.0, 5.0},
{6.0, 7.0, 8.0, 9.0, 10.0},
{11.0, 12.0, 13.0, 14.0, 15.0}},
{{16.0, 17.0, 18.0, 19.0, 20.0},
{21.0, 22.0, 23.0, 24.0, 25.0},
{26.0, 27.0, 28.0, 29.0, 30.0}}
};
for (row = 0; row < 2; row++)
for (column = 0; column < 3; column++)
for (table = 0; table < 5; table++)
printf("values[%d][%d][%d] = %f\n", row, column, table,
values[row][column][table]);
return 1;
}
在C语言中也有一个库函数—–atexit(),它的作用就是令程序在退出main()函数之后彻底终止之前自动完成的一些特定的工作,atexit()库函数中记录了指向这些完成特定操作函数的指针(在main之后执行)
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
36
A
在c语言库函数中将一个字符转换成整型的函数是atol()吗,这个函数的原型是什么?
答案:函数名: atol功能: 把字符串转换成长整型数 用 法: long atol(const char *nptr);
long q;
char *str = "432";
q= atol(str);
printf("string = %s integer = %d\n", str, q); // q=432
软件测试都有那些种类?
答案:黑盒:针对系统功能的测试 白合:测试函数功能,各函数接口
volatile关键字详解
就像大家更熟悉的const一样,volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。
第一
编译器优化问题:
volatile的作用是: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.
简单地说就是防止编译器对代码进行优化.比如如下程序:
XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;
对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,则编译器会逐一的进行编译并产生相应的机器代码(产生四条代码).
第二
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份(在本次线程内,当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后再取变量值时,就直接从寄存器中取值;)。
下面是volatile变量的几个例子:
当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
volatile应该解释为“直接存取原始内存地址”比较合适
C++是不是类型安全的?
答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。
int i=7; printf("%d\n", i++ *i++);
printf("%d\n", i);
分别打印 49和 9
"."如果被重载,就不能调用类成员了,因此不能重载。
“::”如果被重载,则没有办法指明名字空间,因此不能重载。
“?:”这个重载没有意义吧?就像if,else,你重载他干什么...,因此也不能重载
不可重载的运算符集合:{.,.*,::,?:}
C++不能重载的运算符有五个
(1) "."(成员访问运算符)
(2)" .*"(成员指针访问运算符)
(3) "::"(域运算符)
(4)"siezof"(长度运算符)
(5) " ?:"(条件运算符)
37
内存溢出和内存泄露有什么区别?
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
strstr()函数用来检索子串在字符串中首次出现的位置,其原型为:
char *strstr( char *str, char * substr );
str为要检索的字符串,substr为要检索的子串。
返回字符串str中第一次出现子串substr的地址;如果没有检索到子串,则返回NULL。
int a,b;
char *str = "a23456789seesee.com";
char *substr = "see";
char *s = strstr(str, substr);
printf("%s\n", s);
a=strlen(str);
b=strlen(s);
printf("%d\n", a-b); // printf("%d\n", s-str); 可以直接地址相减
exit() 函数与 _exit() 函数的最大区别在于exit()函数在调用exit 系统调用前要检查文件的打开情况,把文件缓冲区中的内容写回文件。也就是图中的“清理I/O缓冲”。
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t result;
result = fork();
if(result<0)
perror("fork");
if(result == 0)
{
printf("This is _exit test\n");
printf("This is the content in the buffer000");
_exit(0);
}
else
{
printf("This is exit test\n");
printf("This is the content in the buffer");
exit(0);
}
return 0;
}
子进程中运行_exit(0)并未将Thisis the content in the buffer000 打印出来,而父进程中运行的exit(0)将Thisis the content in the buffer打印出来了。说明,exit(0)会在终止进程前,将缓冲I/O内容清理掉,所以即使printf里面没有 \n也会被打印出来,而_exit(0)是直接终止进程,并未将缓冲I/O内容清理掉,所以不会被打印出来。
38
A
重复问题
位图法 bit-map
题目:给40个不重复的数 没排序的 然后再给一个数 如何快速判断这个数是不是
在中间
用位图/Bitmap的方法,申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。(实现快速查找这个数是否在中间)
排序问题
一个文件中有9亿条不重复的9位整数 对这个文件中的数字进行排序
解答:
采用位图法 9位整数最大是 999999999 由于9亿是不重复的 把这个组成一个数组
让他有 0---999999999(共10亿)个元素下标表示 0 表示无 1 有 需要的内存为 1G/8=120M左右内存 例如读到2412 就把下标为2412的数组位置1 最后把数组位1的下标存入文件 最终得出排序后的内容
B
Top K问题:在海量数据中找出出现次数最多的K个 最大的K 等等
有1亿个浮点数 找出出现次数最多的10000个
解答:
第一:正常排序 复杂度至少为n*lgn
第二:局部淘汰法
用一个容器装1000个 最后复杂度为 n+m*m m为容器的大小
第三:最小堆法
如 创建大小为1000的堆 复杂度m*lgm
取后面的数依次和堆顶比较 再调整
复杂度 n*lgm 空间复杂度为1000的整数倍
第四:分治法
將一亿个数据分成100份 每份100万 每份找出最大的一万个
第五 Hash法
适用范围:快速查找,删除的基本数据结构,通常需要总数据量可以放入内存
hash统计:先对这批海量数据预处理(维护一个Key为Query字串,Value为该Query出现次数的HashTable,即Hashmap(Query,Value),每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可。最终我们在O(N)的时间复杂度内用Hash表完成了统计;
hash_map(统计频率)
如果这一亿个数里面有很多重复的 先通过hash法去重复 再用别的方法
经常的方法:分而治之/Hash映射?+?Hash统计?+?堆/快速/归并排序
39
s = (char*)realloc(s, sizeof(char)*n);
指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
40
13、typedef char* String_t; 和 #define String_d char * 这两句在使用上有什么区别?
答:typedef char* String_t 定义了一个新的类型别名,有类型检查。
而#define String_d char * 只是做了个简单的替换,无类型检查,前者在编译的时候处理,后者在预编译的时候处理。
同时定义多个变量的时候有区别,主要区别在于这种使用方式String_t a,b;
String_d c,d; a,b ,c都是char*类型,而d为char类型
由于typedef还要做类型检查,#define没有,所以typedef比#define安全。。
计算机在内存中存储数据时使用了大、小端模式,请分别写出A=0X123456在不同情况下的首字节是,大端模式:0X12 小端模式:0X56 X86结构的计算机使用 小端 模式。
int c[4][5]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
int (*p)[5];
p=c;
printf("%d\n",*(*(p+2)+3)); //答案14
int a[3][2]={1,2,3,4,5,6,},*p[3];
p[0]=a[1];
printf("%d\n",*(p[0]+1)); //答案 4
int num = 10;
char str[100];
itoa(num, str, 8); //将整数10转换为八进制保存在str字符数组中
printf("%s\n", str);
互换两个变量 a=a+b;b=a-b;a=a-b;
纯虚函数是怎样实现的?在编译原理上讲一下?
答:在类内部添加一个虚拟函数表指针,该指针指向一个虚拟函数表,该虚拟函数表包含了所有的虚拟函数的入口地址,每个类的虚拟函数表都不一样,在运行阶段可以循此脉络找到自己的函数入口。
抽象类为什么不能实例化?
抽象类中的纯虚函数没有具体的实现,所以没办法实例化。
41 复杂指针
A
(讲究数组元素)数组指针 int (*(*func)[5])(int *p);
func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。
总结一下,就是:
func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,返回值为int类型的函数。
B
(讲究返回值)函数指针 int (*(*func)(int *p))[5];
func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组
从里到外
int (*(*func)(int *p))[5];
可以这样分解:
typedef int (*PARA)[5];
typedef PARA (*func)(int *);
int (*(*x)(int *,char *))(int);
:声明了一个函数指针,这个函数接收一个整型指针和一个字符指针作为参数,返回函数指针,这个函数指针指向的函数接收一个整型参数,返回一个整型。
int(*(*p)(void))[10]; // p是一个函数指针,它指向一个函数,这个函数不接收参数,并且返回一个数组指针,这个指针指向10个int的数组。
C
(讲究指针) 指针数组 char * (*pf[3])(char * p);
这是定义一个函数指针数组。
它是一个数组,数组名为pf,数组内存储了3个指向函数的指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。
D
(讲究返回值)指针函数
int *(*p(int))[3];//可以先跳过,不看这个类型,过于复杂 //从P开始,先与()结合,说明P是一个函数,然后进//入()里面,与int结合,说明函数有一个整型变量//参数,然后再与外面的*结合,说明函数返回的是//一个指针,,然后到最外面一层,先与[]结合,说明//返回的指针指向的是一个数组,然后再与*结合,说//明数组里的元素是指针,然后再与int结合,说明指//针指向的内容是整型数据.
所以P是一个参数为一个整数据且返回
一个指向由整型指针变量组成的数组的指针变量
的函数.
P为函数 形参为int
返回值:返回一个指向 指针数组的 指针
42
http://blog.csdn.net/qiuyunping/article/details/7431111
A
例如一个fp为一个函数指针,(fp代表地址)
则(*fp)()为调用fp指向的的函数
将0转换为--指向返回值为空的函数指针
(void(*)())0
用上面的式子代替fp得到(*(void(*)())0)()
代表硬件会调用地址为0处的子程序。
B
typedef void (*func)();//定义了一个指向返回值是void类型的函数指针
(*(func)0)(); //用上面的指针实现强制转换
typedef void (*func)(void);
func表示一个指向函数的指针类型的名字,该指针类型为”指向返回void类型并且无参数的函数的指针“
可以使用函数名对函数指针进行初始化
typedef void (*func)(void);
void myfunc(void);
func pfun = myfunc;/*赋值*/
pfun();/*调用*/
定义一个函数指针类型。
比如你有三个函数:
void hello(void) { printf("你好!"); }
void bye(void) { printf("再见!"); }
void ok(void) { printf("好的!"); }
typdef void (*funcptr)(void);
这样就构造了一个通用的函数
funcptr words[3] = {&hello, &bye, &ok};
funcptr fun = words[2];
(*fun)();//调用函数
43
A
通过地址调用函数的执行(几种方式)
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
typedef void (*fun)();
void p()
{
printf("mmm\n");
}
int main()
{
void (*ptr)();//函数指针
p();
printf("%x\n",p);//打印函数的入口地址
ptr=(void(*)())0x11f11fe;
ptr();
((void(*)())0x11f11fe)();
((fun)0x11f11fe)();
getchar();
getchar();
}
答案
mmm
0x11f11fe
mmm
mmm
mmm
B
通过地址修改变量的值
int x;
int *p;
printf("%x\n",&x);
p=(int *)0x0012ff60;
*p=3;
printf("%d\n",x);
答案 3
C
如何用宏求结构体的内存偏移地址 (考虑字节对其)
#define FIND( struc, e ) (size_t)&(((struc*)0)- >e)
(struc*)0 //表示将常量0强制转化为struc *型指针所指向的地址 是一个地址
((struc*)0)- >e 表示在0地址处的结构体struc的成员e
&(((struc*)0)- >e)//表示取结构体指针(struc*)0的成员e的地址,
因为该结构体的首地址为0,所以其实就是得到了成员e距离结构体首地址的偏移量.
(size_t)//是一种数据类型,为了便于不同系统之间移植而定义的一种无符号型数据,一般为unsigned int
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
#define FIND(type,e ) ((size_t)&((type*)0)->e)
struct student
{
char a;
int b;
char c[30];
int d;
};
int main()
{
printf("%d\n",FIND(student,a));
printf("%d\n",FIND(student,b));
getchar();
getchar();
}
答案 0 4
扩展一下,现在知道这个变量的地址,求结构体的首地址
#include <stdio.h>
#define FIND(struct,pt,e ) ((int)pt-(int)(&(((struct*)0)->e)))
struct student
{
int a;
int b;
char c[30];
int d;
};
int main()
{
struct student std;
int *m = &std.b;
printf("%d\n",m);
printf("%d\n",FIND(student,m,b));
printf("%d\n",&std.a);
}
答案:x+4 x x
E
指针强制转换后与地址进行加法运算
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
struct student
{
long num;
char *name;
short int data;
char ha;
short ba[5];
};//总共大小24 四字节对其
int main()
{
struct student *p;
p=(struct student *)0x1000000;
printf("%d\n",p);
printf("%d\n",p+200);
printf("%d\n",(char *)p+200);
printf("%d\n",(unsigned long *)p+200);
getchar();
getchar();
}
运行答案:
X X+24*200 x+200 x+4*200
44
A
printf()函数返回值为打印字符个数
int i=4322;
printf("%d\n",printf("%d\n",i));
打印结果: 4322 5(代表i有四个字符加上回车共5个)
B
数组下标为负数
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a[5]={1,2,3,4,5};
int *p=&a[4];
for(int i=-4;i<=0;i++)
printf("%d %d\n",p[i],&p[i]);
printf("%d\n",&a[-2]);//当前地址向前移动 2单位(8字节)
getchar();
getchar();
}
答案: 1 X 2 X+4 3 X+8 4 X+12 5 X+16
X-8
C
我们要切记,对指针进行加1操作,得到是下一个元素的地址,而不是原有地址的数值直接加1,这点大家肯定都知道。假设类型为x,则加1后,指针向后移动sizeof(x),移动是以sizeof(x)为单位的
&a上面也说过了,把a看成一个整体,所以&a + 1是a下一个对象的地址,即&a + 1,
int a[5] = {1, 2 ,3 , 4, 5};
int *m = (int *)&a[0];
printf("%d\n", *(m + 1));
int *p = (int *)&a;
printf("%d\n", *(p + 1));
int *q = (int *)(&a + 1);
printf("%d\n", *(q-1));
int *w = (int *)(&a[0] + 1);
printf("%d\n", *(w-1));
答案: 2 2 5 1
D
int *p1 = new int[10];
int *p2 = new int[10]();
第一个动态申请的空间里面的值是随机值,第二个进行了初始化,里面的值为0
函数指针的好处
如果赋了不同的值给函数指针,那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。
函数指针的用途:一旦函数可以通过指针被传递、被记录,这开启了许多应用,特别是下列三者:
1多态(polymorphism):指用一个名字定义不同的函数,这函数执行不同但又类似的操作,从而实现“一个接口,多种方法”。
2、多线程:将函数指针传进负责建立多线程的API中:例如win32的CreateThread(...pF...)。
3。回调:所谓的回调机制就是:当发生某事件时,自动呼叫某段程序代码。事件驱动的系统经常透过函数指针来实现回调机制,例如Win32的WinProc其实就是一种回调,用来处理窗口听讯息。
求取字符串长度,不使用while、for等循环语句和字符串处理函数。
递归思想
int mystrlen(const char *str)
{
if(*str=='\0')
return 0;
else
return 1+mystrlen(str+1);
}
//应该填入!printf("Hello "), 会先打印出Hello ,然后进行if()判断 !printf()就是0,所以不成立只能运行else,接着打印出World
int main(void)
{
if() // !printf("Hello ")
{
printf("Hello ");
}
else
{
printf("World");
}
return 0;
}
45
A
已知道随机函数rand7(),如何构造rand10()函数
rand7() 返回1 2 3 4 5 6 7之间的一个随机数
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
int rand7()
{
return rand()%7+1;
}
int rand10()
{
int x=0;
do
{
x=(rand7()-1)*7+rand7();
}
while (x>40);
return x%10+1;
}
int main()
{
srand(unsigned(time(0)));
for(int i=0;i!=10;i++)
cout<<rand10()<<" ";
cout<<endl;
getchar();
getchar();
}
输出结果 9 7 10 8 7 8 9 4 5 8等等之类的
B
(void *)ptr和(*(void**))ptr的结果是否相同?
其中ptr为同一个指针答案:.(void *)ptr和 (*(void**))ptr值是相同的
这不是指针函数 这是强制类型转化
第一个没问题 就是把ptr强制转化为指向空类型的指针
第二个(*(void**))ptr
(void**)ptr 先看里面的 这是转化成指向void指针的指针 也就是二级指针
再在前面加上*就是取内容 那么内容也是个指针
这个指针是指向空类型的 所以强制转化后和第一个是一样的
C
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a[5]={1,2,3,4,5};
int b[100];
int *p=(int *)(&a+1);//&a 数组指针 a数组首地址
printf("%d %d\n",*(a+1),*(p-1));//2 5
printf("%d\n",sizeof(b));// 400
printf("%d\n",sizeof(&b));// 4
getchar();
getchar();
}
46
A
嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
一个较晦涩的方法是:
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二种方案,但我建议你在面
B
36.?如何打印出当前源文件的文件名以及源文件的当前行号??
答案:
cout?<<?__FILE__??
cout<<__LINE_
__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。
C
?下面的代码输出是什么,为什么
?void?foo(void)??
{?unsigned int a=6;
int b=-20;
(a+b > 6) ? puts("> 6") : puts("<= 6");??
}?
C中运算有规定,如果整型变量间进行数据运算,只要有一个变量是无符号的,结果就按无符号数据输出,因此a+b > a
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是“>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。?因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。
D
嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a
的bit 3。在以上两个操作中,要保持其它位不变。
#define BIT3(0x1<<3)
static int a;
void set_bit3(void)
{
a|=BIT3;
}
void clear_bit3(void)
{
a &=~BIT3;
}
E
字符格式
unsigned int a=0xfffffff7;
unsigned char i=(unsigned char)a;
char *b=(char*)&a;
printf("%08x\n",i); //000000f7
printf("%08x\n",*b);//fffffff7
printf("%d\n",i); //247
printf("%d\n",*b);//-9
unsigned int a=0xfffffff7;
char i=(char)a;
printf("%d\n",i);// -9
47
A
float(**def)[10]
数组指针
def是一个二级指针 他指向一个一个有10个元素的指针 元素为float形
double *(*g)[10]等价于double *((*g)[10])
数组指针
g一个指针 他指向一个一个有10个元素的指针 元素为double * 指针
int (*(*fun)(int,int))(int)
函数指针 指向的函数 形参 为 int int 返回值也是一个函数指针 这个指针形参
为 int 返回值为int
B
int *pa=NULL;
int *pb=pa+15;//pb=0+15*4
printf("%x\n",pb);//%x 16进制 60=3c
C
char * stra()
{
char str[]="hello";
return str;
}//不可以
char * stra()
{
char *str="hello";
return str;
}//可以
char * stra()
{
static char *str="hello";
return str;
}//可以
解释:
char str[]="hello";是分配一个局部数组 内存中的栈 str[0]='t' 可以修改
char *str="hello";是分配一个全局数组 内存中的全局去 *str="rr"错误 不能修改
static char *str="hello";是分配一个全局数组
D
int b=3;
int arr[]={6,7,8,9,10};
int *ptr=arr;
*(ptr++)+=123;
printf("%d %d\n",*ptr,*(++ptr));
答案 8 8
解释: *(ptr++)+=123;等价于 *(ptr)+=123; ptr++;
printf语句执行顺序 从右往左 打印从左往右
E
内连函数
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
inline int f(int i) {return i*i;};
void main()
{
printf("%d\n",f(9));
getchar();
getchar();
}
F
指针强制转换
int a=8;
int b=9;
int *p=&a;
float *d=(float *)&b;
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
int a=3;
void main()
{
int a=10;
::a++;//全局
printf("%d\n",::a); //全局 4
printf("%d\n",a);//局部 10
getchar();
getchar();
}
int a,x;
for(a=0,x=0;a<=1&&!x++;)
a++;
cout<<a<<" "<<x<<endl;// 1 2
int a=10,x=3,z=3;
a==(x=z=3);
printf("%d\n",a);// a=10 a不等于3 仍然为10
48
A
二维数组的地址
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
printf("%d,%d,%d,%d,%d\n",a,*(a+0),a[0],&a[0],&a[0][0]); // X
printf("%d,%d,%d,%d,%d\n",a+1,*(a+1),a[1],&a[1],&a[1][0]);//X+16
printf("%d,%u,%d,%d,%d\n",a+2,*(a+2),a[2],&a[2],&a[2][0]);//X+32
printf("%d,%d\n",a[1]+1,*(a+1)+1); //X+20
printf("%d,%d\n",*(a[1]+1),*(*(a+1)+1)); // 5 5
getchar();
getchar();
}
B
数组指针
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int i,j,(*p)[4];
p=a;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
printf("%d ",*(*p+j));//等价那种 *(*(p+i)+j)
}
p++;
printf("\n");
}
getchar();
getchar();
}
打印
1,3,5,7
9,11,13,15
17,19,21,23
C
指向二维数组的指针变量
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int *p;
int *q;
for(p=a[0];p<a[0]+12;p++)
{
if((p-a[0])%4==0) printf("\n");
printf("%d ",*p);
}
printf("\n");
q=a[0];
q++;
printf("%d\n",q-a[0]);//注意等于1 指针相减为元素的个数
getchar();
getchar();
}
打印:
1,3,5,7
9,11,13,15
17,19,21,23
1
D
双重指针
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int *b[3]={a[0],a[1],a[2]};
int **p=b;
int i,j;
for(i=0;i<3;i++)
for(j=0;j<4;j++)
printf("%d,%d,%d,%d\n",a[i][j],*(*(a+i)+j),*(b[i]+j),*(*(p+i)+j));//打印相同的数字
getchar();
getchar();
}
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
char *ps[3]={"aaa","bbb","ccc"};
char **pp;
pp=ps;
for(int i=0;i<3;i++)
puts(*pp++);
getchar();
getchar();
}
打印结果
aaa
bbb
ccc
char * a[]={"hello","the","word"};
char **p=a;
p++;
cout<<*p<<endl;
打印结果 the
49
A
内部函数和外部函数
内部函数又称静态函数 他只能在定义他的源文件中被调用 而不能被其他源文件中的函数调用 定义内部函数时候 需要在函数首部最左端加关键字static
外部函数 凡是不被说明为内部函数的的函数 均是外部函数 外部函数可以被其他源文件中的函数调用
定义时候可以加extern显示说明 不加也可
内部函数
如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。
定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,如下所示:
static 函数类型 函数名(函数参数表)
{… …}
关键字“static”,译成中文就是“静态的”,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。
使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。
外部函数
外部函数的定义:在定义函数时,如果没有加关键字“static”,或冠以关键字“extern”,表示此函数是外部函数:
[extern] 函数类型 函数名(函数参数表)
{……}
调用外部函数时,需要对其进行说明:
[extern] 函数类型 函数名(参数类型表)[,函数名2(参数类型表2)……];
例 外部函数应用。
(1)文件mainf.c
main()
{ extern void input(…),process(…),output(…);
input(…); process(…); output(…);
}
(2)文件subf1.c
extern void input(……)
{……}
(3)文件subf2.c
extern void process(……)
{……}
(4)文件subf3.c……
extern void output(……)
{……}
例 数组排序----简单选择排序
file1.c
main()
{
extern void sort(int array[ ],int n);
int a[10],i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
file2.c
void sort(int array[],int n)
{ int i,j,k,t;
for(i=0;i<n-1;i++)
{ k=i;
for(j=i+1;j<n;j++)
if(array[j]<array[k]) k=j;
if(k!=i)
{ t=array[i];
array[i]=array[k];
array[k]=t;
}
}
}
B
typedef的使用
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
typedef int COUNT[10];//定义COUNT为int 数组
void main()
{
COUNT a={1,2,3};
printf("%d \n",a[0]);// 1
getchar();
getchar();
}
typedef和define的区别
typedef int * STR;
int a=3,b=8;
STR c ,d;
c=&a; d=&b;
printf("%d \n",*c);
printf("%d \n",*d);
#define STR int*
int a=3,b=8;// STR c,d;是错误的 因为只能代表C为指针 D不是
STR c ;
STR d;
c=&a; d=&b;
printf("%d \n",*c);
printf("%d \n",*d);
C
迷途(stray)指针,也被称为野(wild)指针或悬浮(dangling)指针,是指将delete 用于指针(从而释放它指向的内存),但没有将它设置为空时引发。如果随后你在没有重新赋值的情况下使用该指针,后果将是不可预料的:程序崩溃算你走运。
通常delete一个指针后 再对这个指针PTR=0;这样可以赋为空指针,这样就消除迷途指针带来的危害
“野指针”产生原因及解决办法如下:
?
(1)指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向NULL。?
(2)指针?p?被?free?或者?delete?之后,没有置为?NULL。解决办法:指针指向的内存空间被释放后指针应该指向NULL。
?
(3)指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向NULL。
int *ptr;
ptr=(int *)0x8000;
*ptr=3;
解答:指针问题,首先将指针ptr指向一个指定的地址,即对一个未作声明的地址直接进行访问,所以访问出错。这块地址可能已经被用,或者被其他程序占用等,因此会导致错误。
50
A
Register变量:动态和静态变量都是存放在内存中,程序中遇到该值时用控制器发指令将变量的值送到运算器中,需要存数再保存到内存中。如果频繁使用一个变量,比如一个函数体内的多次循环每次都引用该局部变量,我们则可以把局部变量的值放到CPU的寄存器中,叫寄存器变量。不需要多次到内存中存取提高效率。
寄存器变量没有地址 若n为寄存器变量 以&n引用n是错误的表示形式
一个计算机中寄存器数量有限 不能任意定义多个寄存器变量
只有局部自动变量和形参可以定义为寄存器变量
语言中包括了关键字auto,它可用于定义局部变量。但自从所有的非全局变量的缺省值假定为auto以来,auto就几乎很少使用了。 在C或者以前的C++中,auto关键字基本上可以被无视:比如这个局部变量:int a = 100;auto int a = 100;并没有什么区别。
函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。
B
静态全局变量
static全局变量与普通的全局变量有什么区别 ?
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。
全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。
这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
static全局变量只初使化一次,防止在其他文件单元中被引用;
注意:全局变量和全局静态变量的区别
1)全局变量是不显式用static修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。
2)全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。
静态局部变量
只初始化一次 这次保留上次的值使用
C
局部变量和全局变量
例如文件a.c需要引用b.c中的变量int v,就可以在a.c中申明extern int v;这样就可以使用
a.h b.c c.c 其中a.h分别被包含在 b.c和c.c中 如果要定义一个在b c都能使用的变量 若在a.h中定义是不行的(出现重复定义错误) 只能在其中一个C文件中定义 另外一个用extern引用
51
C语言文件操作
A
给桌面一个文件写入消息
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
FILE *fp;
char ch,filename[10];
if((fp=fopen("D:\\Users\\litingting\\Desktop\\aaa.txt","w"))==NULL)
{
printf("error");
exit(0);
}
for(;(ch=getchar())!='@';)
fputc(ch,fp);
fclose(fp);
getchar();
getchar();
}
读消息
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
FILE *fp;
char ch,filename[10];
if((fp=fopen("D:\\Users\\litingting\\Desktop\\aaa.txt","r"))==NULL)
{
printf("error");
exit(0);
}
for(;(ch=fgetc(fp))!=EOF;)//每次读取一个字符存入ch
putchar(ch);
putchar('\n');
fclose(fp);
getchar();
getchar();
}
52
A
C/C++语言中的main函数,经常带有参数argc,argv,如下:
int main(int argc, char** argv)
这两个参数的作用是什么呢?argc 是指命令行输入参数的个数,argv存储了所有的命令行参数。假如你的程序是hello.exe,如果在命令行运行该程序,(首先应该在命令行下用 cd 命令进入到 hello.exe 文件所在目录)
运行命令为:hello.exe Shiqi Yu
那么,argc的值是 3,
argv[0]是"hello.exe",argv[1]是"Shiqi",argv[2]是"Yu"。
B
a=*p++
解释如下:
*p++=*(P++) 等价于 a=*p P++
优先级++高于*
#include "stdafx.h"
#include "stdio.h"
void main(){
int a[5]={100,2,3,4,5};
int *p = a;
int b=0,c=0;
b=*(p++);
c=*p;
printf("%d %d",b,c);
getchar();
getchar();
getchar();}
2
原码
就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
反码
的表示方法是:
正数的反码是其本身
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
补码
的表示方法是:
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
3
全局变量没初始化(静态) 默认为0
非静态局部变量 没初始化 则这个值 随机的
4
extern关键字
也就是说extern有两个作用,
第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非
第二,当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块活其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。
extern(叫声明 不叫定义)
在源文件A里定义的函数,在其它源文件里是看不见的(即不能访问)。为了在源文件B里能调用这个函数,应该在B的头部加上一个外部声明:
extern 函数原型;
这样,在源文件B里也可以调用那个函数了
举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。能够被其他模块以extern修饰符引用到的变量通常是全局变量
5
A
int (*a)(int)
一个指向函数的指针 该函数有一个整形参数 并返回一个整形数
int (*a[10])(int)
一个有10个指针的数组 该指针指向一个函数 该函数有一个整形数并返回一个整形数
B
(*a)[10]数组指针 和 *a[10] 指针数组
6
编程技巧、
while(i=0)循环结束
while(i=1) 死循环
int a=5 b=4 c=3
\a>b>c 的值为0 先a>b
int a=(b=(c=020)&&(1==2)) 先c=020在&&0 所以b=0 a=0 等号(=)的优先级最低
7
printf 语句
#include <stdio.h>
void main()
{
int a=5,b=2;
printf("%d %d\n",b=a+1,a=a+1);
}
输出结果是:7 6
而不是:6 6
这是因为printf函数的计算是从右向左进行的。
8 短路求值
(条件1&&条件2) 如果条件1满足 条件2的语句不执行
9
char *c1 = "abc";实际上先是在文字常量区分配了一块内存放"abc",然后在栈上分配一地址给c1并指向
这块地址,然后改变常量"abc"自然会崩溃
然而char c2[] = "abc",实际上abc分配内存的地方和上者并不一样,可以从
char c[]="hello word" 是分配一个局部数组
char* c="hello word" 是分配一个全局数组、
因为这个函数返回的是一个局部变量地址,当调用这个函数后,这个局部变量str 就释放了 所以返回的结果是不安全的 不稳定的 随时度有可能被收回的可能
10
int *ptr;
ptr=(int *)0x8000;//
*ptr=3;
解答:指针问题,首先将指针ptr指向一个指定的地址,即对一个未作声明的地址直接进行访问,所以访问出错。这块地址可能已经被用,或者被其他程序占用等,因此会导致错误。
在一个不确定的地址上面进行直接访问,导致出错
数组名本身就是指针 再加& 就变双指针了 这里的双指针就变成二维数组 加1 就是整体加一行
数组做形参时候 就退化为指针了
11
逗号表达式
(表达式1,表达式2,表达式3)计算从左往右 整体结果为最后一个表达式的结果
12 预编译
在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时,就会出现大量重定义的错误。在头文件中实用#ifndef #define #endif能避免头文件的重定义。
13 extern 关键字用法
例如文件a.c需要引用b.c中的变量int v,就可以在a.c中申明extern int v;这样就可以使用
a.h b.c c.c 其中a.h分别被包含在 b.c和c.c中 如果要定义一个在b c都能使用的
变量 若在a.h中定义是不行的 只能在其中一个C文件中定义 另外一个用extern引用
14 全局变量 静态全局变量 静态局部变量 局部变量 总结
A 全局变量
(a)若程序由一个源文件构成时,全局变量与全局静态变量没有区别。
(b)若程序由多个源文件构成时,全局变量与全局静态变量不同:全局静态变量使得该变量成为定义该变量的源文件所独享,即:全局静态变量对组成该程序的其它源文件是无效的,那么作用域就在本文件,其他.c或.h文件要用到此变量,用extern 也是不行的。
(c)具有外部链接的静态;可以在所有源文件里调用;除了本文件,其他文件可以通过extern的方式引用;
B 静态全局变量的作用:
(a)不必担心其它源文件使用相同变量名,彼此相互独立。
(b)在某源文件中定义的静态全局变量不能被其他源文件使用或修改。
(c) 只能在本文件中使用!具有内部链接的静态;不允许在其他文件里调用;
C 静态局部变量
静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见
D 局部变量
局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
E 总结:
全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间
15 不用sizeof来判断系统的位数
//无符号,若为16微系统 则0-65535 对0取反后111111111111111为65535
若为32 1111111111111111111111111111111肯定大于65536
void main()
{
unsigned int a=~0;
if(a>65536)
printf("32");
else
printf("16");
getchar();
getchar();
}
// 在32位系统下 输出65536 65535
// 在16位系统下 输出 0 -1
void main()
{
unsigned int i=65536;
unsigned int j=65535;
//在内存中存补码 1111111111111111 减1的11.。。。10 取反100---01(源码) 所以打印-1
printf("%d\n",j);
printf("32系统");
getchar();
getchar();
}
%u 无符号输出 %d 有符号输出、
i*-1代表-i
16 内存分配方式
[1]从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
[2]在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
[3]从堆上分配,亦称动态内存分配。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
17 C++虚拟函数表
博客地址;http://blog.csdn.net/haoel/article/details/1948051/
A 虚函数基础
typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
// Invoke the first virtual function
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
实际运行经果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)
虚函数表地址:0012FED4
虚函数表 — 第一个函数地址:0044F148
Base::f
B 虚函数进阶(已经过编译成功)
#include "stdafx.h"
#include <iostream>
using namespace std;
class Base1 {
public:
virtual void f() { cout << "Base1::f" << endl; }
virtual void g() { cout << "Base1::g" << endl; }
virtual void h() { cout << "Base1::h" << endl; }
};
class Base2 {
public:
virtual void f() { cout << "Base2::f" << endl; }
virtual void g() { cout << "Base2::g" << endl; }
virtual void h() { cout << "Base2::h" << endl; }
};
class Base3 {
public:
virtual void f() { cout << "Base3::f" << endl; }
virtual void g() { cout << "Base3::g" << endl; }
virtual void h() { cout << "Base3::h" << endl; }
};
class Derive : public Base1, public Base2, public Base3 {
public:
virtual void f() { cout << "Derive::f" << endl; }
virtual void g1() { cout << "Derive::g1" << endl; }
};
typedef void(*Fun)(void);
int main()
{
Fun pFun = NULL;
Derive d;
int** pVtab = (int**)&d;
//Base1's vtable
//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0);
pFun = (Fun)pVtab[0][0];
pFun();
//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1);
pFun = (Fun)pVtab[0][1];
pFun();
//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2);
pFun = (Fun)pVtab[0][2];
pFun();
//Derive's vtable
//pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3);
pFun = (Fun)pVtab[0][3];
pFun();
//The tail of the vtable
pFun = (Fun)pVtab[0][4];
cout<<pFun<<endl;
//Base2's vtable
//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);
pFun = (Fun)pVtab[1][0];
pFun();
//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);
pFun = (Fun)pVtab[1][1];
pFun();
pFun = (Fun)pVtab[1][2];
pFun();
//The tail of the vtable
pFun = (Fun)pVtab[1][3];
cout<<pFun<<endl;
//Base3's vtable
//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0);
pFun = (Fun)pVtab[2][0];
pFun();
//pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1);
pFun = (Fun)pVtab[2][1];
pFun();
pFun = (Fun)pVtab[2][2];
pFun();
//The tail of the vtable
pFun = (Fun)pVtab[2][3];
cout<<pFun<<endl;
getchar();
getchar();
return 0;
}
构造函数不能为虚函数?
构造函数不能为虚函数的原因如下:
从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。
析构函数可以为虚函数 而且通常申明为虚函数 ?是的
博客地址 http://www.cnblogs.com/lixiaohui-ambition/archive/2012/07/13/2589716.html
在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。
内联函数不能为虚函数?是的
如果函数已经被声明为inline, 内联函数已经在编译期间它的调用点上就被展开;而虚拟函数调用的决定则要等到运行时刻在执行程序内部的每个调用点上系统根据被调用对象的实际基类或派生类的类型来决定选择哪一个虚拟函数实例,因此虚拟函数在编译期间并不能被展开,所以内联函数不能成为虚拟函数.
静态成员函数不能为虚函数? 是的
虚函数是为了多态设计的,静态成员函数独立于对象存在,没有this指针,所以不能设计成虚函数,而类的普通成员函数(包括虚函数)在编译时加入this指针,通过这种方式可以与对象捆绑,而静态函数编译时不加this,因为静态函数是给所有类对象公用的,所以没有在编译时加this,所以无法与对象捆绑,而虚函数就是靠着与对象捆绑加上虚函数列表才实现了动态捆绑。所以没有this指针虚函数无从谈起。
友元函数
对于没有继承特性的函数没有虚函数的说法
18
float与0值的比较
无论是float还是double 他们在内存中存储机制和整形数不同 有舍入差、
浮点运算可交换,但是不满足结合律
#include "stdafx.h"
#include<iostream>
using namespace std;
void main(int argc, char* argv[])
{
float x=0.000009;
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON))
printf("x=0\n");
else printf("x!=0\n");
getchar();
getchar();
}
19
A
char string[10];
char* str1="0123456789";
strcpy(string,str1);
str1里面有11个字符(外加\0)
而string[10]只有10个字符
所以出现越界情况
B
char * str1
char string[10]
if(strlen(str1)<=10)
{
strcpy(string,str1);
}
由于strlen不计算\0 所以应改为strlen(str1)<10
19
const 用法
主要作用定义数据为常类型
一、指向常量的指针
定义: const 数据类型 *指针变量名
含义: 一个指向常量的指针变量。
说明: 此指针可更改指向其他的常量,但此指针所指向的常量不能被更改。
例子
const char *name = "chen"; //声明指向常量的指针name
name[3] = 'a'; //出错,指针所指向的变量的值不能改变
name = "zhang"; //合法,指针自身的值可改变
(另一种表达式 char const *name)
二 常指针
定义: 数据类型 *const 指针变量名 = 初始化值
含义: 一个指向变量的不可改变的指针变量。
说明: 在定义常指针时必须初始化;
此指针的值不可改变,但此指针所指向的变量值可改变。
例子:
char *const name = "chen"; //声明常指针name
name[3] = 'a'; //合法,指针所指向的变量值可改变
name = "zhang"; //出错,指针自身值不可改变
三 指向常量的长指针
定义: const 数据类型 *const 指针变量名 = 初始化值
含义: 一个指向常量的常指针变量。
说明: 在定义时此指针必须初始化;
此指针的值和它所指向的常量的值均不可被改变。
例子:
const char *const name = "chen"; //声明指向常量的常指针
name[3] = 'a'; //出错,指针所指向的变量值不可改变
name = "zhang"; //出错,指针自身值不可改变
还有就是 const int a=3和int const=3意思完全一样
常引用:
常引用也称const引用,之所以引入常引用,是为了避免在使用变量引用时,在毫不知情的情况下改变了变量的值,从而引起程序错误
const 类型标示符 & 引用名 = 目标变量
断言assert()捕获非法情况 括号里的表达式为真 则继续执行 为假就停止
#define TRACE(S) (printf("%s\n",#S),S) 解释如下
首先 printf("%s\n",#S)代表 printf("%s\n","S")
然后 TRACE(S)这个表达式去S值 这是个逗号表达式 所以取最后的值
20
野指针的避免方法
指针变量没有被初始化时候,解决办法,声明时候初始化,
可以是具体指值,也可以让他指向NULL,
指针P被free()或者delete()之后,没有设置为NULL。解决办法:
指针指向的内存空间被释放之后,指针应该指向NULL
21
综合性问题
char *c1 = "abc";实际上先是在文字常量区分配了一块内存放"abc",然后在栈上分配一地址给c1并指向
这块地址,然后改变常量"abc"自然会崩溃
然而char c2[] = "abc",实际上abc分配内存的地方和上者并不一样,可以从
char c[]="hello word" 是分配一个局部数组
char* c="hello word" 是分配一个全局数组、
因为这个函数返回的是一个局部变量地址,当调用这个函数后,这个局部变量str 就释放了 所以返回的结果是不安全的 不稳定的 随时度有可能被收回的可能
int *ptr;
ptr=(int *)0x8000;
*ptr=3;
解答:指针问题,首先将指针ptr指向一个指定的地址,即对一个未作声明的地址直接进行访问,所以访问出错。这块地址可能已经被用,或者被其他程序占用等,因此会导致错误。
在一个不确定的地址上面进行直接访问,导致出错
数组名本身就是指针 再加& 就变双指针了 这里的双指针就变成二维数组 加1 就是整体加一行
数组指针和指针数组的区别
数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
指针数组
数组做形参时候 就退化为指针了
当派生类中新增加的数据或函数与基类中原有的同名时候 若不加限制 则优先
调用派生类中的成员
基类指针只能从派生类中继承子类部分
22
A
友元函数性质:
关键字 friend开头
友元函数不是成员函数 使用时候不要:: 成员函数使用才要这个符号
友元函数不能直接访问类的成员 只能访问对象成员
友元函数可以访问对象私有成员 但是普通函数不行
调用友元函数时候 在实际参数中指明要访问的对象
类与类之间的友元关系不能继承 成员函数才要继承之说
B
拷贝构造函数
该函数名与类同名 也是一种构造函数
该函数只有一个参数 并且是某个对象的引用
每个类必须有一个拷贝构造函数
用户没定义 系统会自动生成一个缺省拷贝构造函数
拷贝构造函数的目的是创建一个新的对象实体 一定要争新的对象有自己的空间
而不是与先前对象共用
CA(const CA& C)就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(X& x)。
当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
深拷贝和潜拷贝
如果一个类拥有资源,当这个类在发生复制过程中 资源重新分配 称为深拷贝
反之对象存在资源 但在复制过程并没复制资源的情况为潜拷贝
基类的构造函数和析构函数度不能被派生类继承
类的成员变量的初始化顺序只与变量在类中申明的顺序有关 与在构造函数中初始化顺序
无关。
23
数组和指针的区别
A 修改内容
字符数组a的容量是6个字符,其内容为hello\0。a的内容可以改变,如a[0]= ‘X’。指针p指向常量字符串“world”(位于静态存储区,内容为world\0),常量字符串的内容是不可以被修改的。从语法上看,编译器并不觉得语句p[0]= ‘X’有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。
char a[] = “hello”;
a[0] = ‘X’;
cout << a << endl;
char *p = “world”; // 注意p指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误 运行错误
cout << p << endl;
B
用运算符sizeof可以计算出数组的容量(字节数)。示例3(a)中,sizeof(a)的值是12(注意别忘了’\0’)。指针p指向a,但是sizeof(p)的值却是4。这是因为sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char*),而不是p所指的内存容量。C++/C语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。
C 注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针
24
请说出const与#define 相比,有何优点?
A const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
B 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
25
堆与栈的区别
堆和栈的区别
一、预备知识—程序的内存分配
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"
优化成一个地方。
}
二、堆和栈的理论知识
申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空
间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = new char[10];
但是注意p1、p2本身是在栈中的。
申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢
出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表
中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的
首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部
分重新放入空闲链表中。
申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意
思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有
的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将
提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储
的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小
受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是
直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。
堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可
执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈
的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地
址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
存取效率的比较
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到
edx中,再根据edx读取字符,显然慢了。
小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就
走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自
由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由
度大。
26
A
一个参数既可以是const还可以是volatile吗?解释为什么。
解:是的。比如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
B
小段模式和大端模式
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
#include <stdio.h>
//判断操作系统是大端还是小端
bool checkendian()
{
union my
{
int a;
char b;
};
my t;
t.a=1;
return(t.b==1);
}
int main()
{
printf("%d\n",checkendian());
return 0;
}
C
下面的代码片段的输出是什么,为什么?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
答案输出:Got a valid pointer
解释:当使用malloc后,只有在没有足够内存的情况下会返回NULL,或是出现异常报告。
malloc(0),系统就已经帮你准备好了堆中的使用起始地址(不会为NULL)。但是你不能对该地址进行写操作(不是不允许),如果写了话,当调用free(ptr)就会产生异常报告(地址受损)
NULL一般预定义为(void *)0,指向0地址。malloc是在程序堆栈上分配空间,不会是0地址 malloc(0)是指分配内存大小为零 NULL是不指向任何实体 malloc(0)也是一种存在不是NULL
27
void main(char (&p)[10])
{
printf("%d\n",sizeof(p));
getchar();
getchar();
}
打印 10
不要在类的构造或者析构过程中调用虚函数,因为这样的调用永远不会沿类继承树往下传递到子类中去。
int main(void) // :X86_64环境
{
int a[4][4] = {
{1,2,3,4},
{50,60,70,80},
{900,1000,1100,1200},
{13000,14000,15000,16000} };
int (*p1)[4] = a;
int (*p2)[4] = &a[0];
int *p3 = &a[0][0];
printf("%d %d %d %d\n",*(*(a+1)-1),*(*(p1+3)-2)+1,*(*(p2-1)+16)+2,*(p3+sizeof(p1)-3));
return 0;
}
解释::
4 1101 13002 60
p1为指向一维数组的指针,所以a + 1指向{50,60,70,80}这一维的地址。减一则为4的地址;同理第二个输出1101。同理,由于数组的列是4,所以*(p2 - 1) + 16就相当于*(p2) + 12,所以第三个输出13002。
第四个由于p1是指针,所以sizeof(p1)为8(68位的系统),所以第四个输出60。
typedef struct object object;
struct object
{
char data[3];
};
int main(void)
{
object obj_array[3] = { {'a','b','c'},
{'d','e','f'},
{'g','h','i'} };
object* cur = obj_array;
printf("%c %c\n", *(char*)((char *)(cur)+2) , *(char*)(cur+2));
return 0;
}
答案:c g
int a[2][3]={1,2,3,4,5,6};
printf("%d\n",*(*(a+1)+1)); 5
printf("%d\n",**(a+1)); 4
printf("%d\n",*(*a+1)); 2
class A
{
public:
int b;
char c;
virtual void print()
{
cout<<"this is father's function!"<<endl;
}
};
class B : A
{
public:
virtual void print()
{
cout<<"this is children's function!"<<endl;
}
};
int main(void)
{
cout<<sizeof(A)<<" "<<sizeof(A)<<endl;
return 0;
}
输出 12 12 (数据按四字节对其)
32位系统
struct st
{
char ch , *ptr;
union
{
short a , b;
unsigned int c : 2 , d : 1;
};
bool f;
struct st *next;
};
大小20字节
采用new操作符创建对象时,如果没有足够内存空间而导致创建失败,则new操作符会返回NULL?
错 new在失败后抛出标准异常std::bad_alloc而不是返回NULL。
28
在Intel CPU上,以下多线程对int型变量x的操作,哪几个不是原子操作,假定变量的地址都是对齐的?(ABC)
A、x = y(两句) B、x++(三句) C、++x(三句) D、x = 1(一句)
一般情况下,下面哪些操作会执行失败?(BCD)
class A
{
public:
string a;
void f1()
{
printf("Hello World");
}
void f2()
{
a = "Hello World";
printf("%s",a.c_str());
}
virtual void f3()
{
printf("Hello World");
}
virtual void f4()
{
a = "Hello World";
printf("%s",a.c_str());
}
};
A、A *aptr = NULL; aptr->f1();
B、A *aptr = NULL; aptr->f2();
C、A *aptr = NULL; aptr->f3();
D、A *aptr = NULL; aptr->f4();
至于A为什么正确,因为A没有使用任何成员变量,而成员函数是不属于对象的,所以A正确。其实,A* aptr = NULL;aptr->f5();也是正确的,因为静态成员也是不属于任何对象的。至于BCD,在B中使用了成员变量,而成员变量只能存在于对象,C有虚表指针,所以也只存在于对象中。D就更是一样了。但是,如果在Class A中没有写public,那么就全都是private,以至于所有的选项都将会失败。(根本没有定义对象)
如果改成 A bbb;
A *aptr = &bbb;
aptr->f1(); 正确
aptr->f2(); 正确
aptr->f3(); 正确
aptr->f4(); 正确
已知*p=NULL, *q=new char[100],sizeof(p) 和 sizeof(q)的值各为多少?
答案 4和4
29
A
char* GetMem()
{
char p[] = "hello";
return p;
}
void test_get_mem()
{
char *p = GetMem();
printf(p);
return ;
}
GetMem函数中的p是一个在栈上的局部变量,当函数运行结束的时候,栈上的内容会自动释放的,此处返回的值有可能会成为一个野指针,会出现一个意想不到的结果。
B
void test_mem()
{
char *p = new char[64];
delete p;
p = NULL;
return ;
}
应该修改为 delete[]p; p指向的是一个字符型的数组空间,原来的代码只是简单的释放了指向申请空间的指针,并没有释放申请的空间,容易造成内存崩溃。
回收用 new 分配的单个对象的内存空间的时候用 delete,回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。
C
class A
{
}; 1
class B
{
public:
B();
virtual ~B();
}; 4 地址指针占了4个字节
class B
{
public:
B();
}; 1
class C
{
private:
#pragma pack(4)
int i;
short j;
float k;
char l[64];
long m;
char *p;
#pragma pack()
};
class D
{
private:
#pragma pack(1)
int i; //4
short j; //2
float k; //4
char l[64]; //64
long m; //4
char *p; //4 double 8
#pragma pack()
};
int main(void)
{
printf("%d\n",sizeof(A));
printf("%d\n",sizeof(B));
printf("%d\n",sizeof(C));
printf("%d\n",sizeof(D));
getchar();
getchar();
return 0;
}
答案 1、4、84、82
以下while循环执行(C)次。(考数的存储 补码形势)
unsigned int k = 20;
while(k >= 0)
--k;
A、20次 B、一次也不执行 C、死循环 D、21次
如果改成 int k=20;
则执行21次
int a=5,则 ++(a++)的值是(D)
A、5 B、 6 C、7 D、逻辑错误
a++返回的是一个临时变量,这里是右值,不能再前面++了
在32位系统中,char str[]="xuelei"; char *p = str; sizeof(str)=() ,sizeof(p)=() ,sizeof(*p)=()
答案分别是: 7、4、1,分别对数组、指针和一个字符类型求大小。。
char a = 255;
printf("%d\n",a);
由于255的二进制表示是1111 1111,将其作为int类型输出的时候,由于最高位是1,表示的是一个负数,其表示的数字就是将最高位后面的7个1取反后在加上1,表示的就是-1,所以第二个输出应该是-1。
若调用一个函数,且此函数中无return语句,则正确的说法是
返回一个不确定的值
30
A
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"A"<<endl;
}
};
int main(void)
{
A a[4], b,*p;
}
输出5个A
p只是一个对象指针,并没有指向一个对象的内存空间,所以没有调用构造函数。
B
如下的字符串函数,用于生存一个字符串 ”连接号码异常” ,并返回它的指针
[cpp] view plaincopy
char* strfun()
{
char str[20];
strcpy(str, “连接号码异常”);
printf(“%s \n”, str); //printf语句1
return str;
}
void main()
{
char *pstr = strfun();
printf("%s \n", pstr); //printf语句2
}
问题1 : printf语句1和printf语句2哪个能在屏幕上正在打印出来?
问题2 : 如果不能正常在屏幕上打印出字符串,请说明原因。
问题3 : 如果不修改strfun的声明,请问该如何修改上述程序的错误。
答:
问题1:语句1可以正常打印,语句2不能正常打印;
问题2:语句2使用的指针所指向的内存空间str[20],在函数strfun返回时已经被释放了;
问题3:可以将函数strfun中的语句char str[20];改为char *str = new char[20];
long不能做switch表达式的值 汉子存储占两个字节
给出以下定义, 则正确的叙述为
char x[]="abcdefg";
char y[]={'a','b','c','d','e','f','g'};
A、数组X和数组Y等价
B、数组X和数组Y长度相同
C、数组X的长度大于数组Y的长度(正确)
D、数组X的长度小于数组Y的长度
printf("%d\n",sizeof(x)); 输出8
printf("%d\n",sizeof(y)); 输出7
因为x数组是字符串数组,后面还有结束符:“\0”,所以长度为:8
而y数组就是普通的字符数组,没有“\0”结束符的,所以长度为:7
定义数组
define N 10;
int x[N]; 正确
int N = 10; //这个是错误的,数组的大小应该是一个常量表达式
int x[N];
const int N = 10; //这个是正确的
int x[N];
31
A
int a=5,则 ++(a++)的值是(逻辑错误)
A、5 B、 6 C、7 D、逻辑错误
a++返回的是一个临时变量,这里是右值,不能再前面++了
B
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char* str5 = "abc";
const char* str6 = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 输出 F
cout << boolalpha << ( str3==str4 ) << endl; // 输出 F
cout << boolalpha << ( str5==str6 ) << endl; // 输出 T
答:
分别输出false,false,true。
str1和str2都是字符数组,每个都有其自己的存储区,它们的值则是各存储区首地址,不等;
str3和str4同上,只是按const语义,它们所指向的数据区不能修改。
str5和str6并非数组而是字符指针,并不分配存储区,其后的“abc”以常量形式存于静态数据区,而它们自己仅是指向该区首地址的指针,所以相等。
C
unsigned int const size1 = 2;
char str1[ size1 ];//正确
unsigned int temp = 0;
cin >> temp;
unsigned int const size2 = temp;
char str2[ size2 ]; //错误
str2定义出错,size2非编译器期间常量,而数组定义要求长度必须为编译期常量。
cout<< (true?1:"0") <<endl;
答:三元表达式“?:”问号后面的两个操作数必须为同一类型
struct Test
{
Test(int ) { }
Test() { }
void fun() { }
};
int main(void)
{
Test a(1);
a.fun();
Test b(); //变量b定义出错。按默认构造函数定义对象,不需要加括号。
b.fun();
return 0;
}
char *GetMemory(void)
{
char *p = "Hello World";
return p; // 可以返回 地址在堆上
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
strcpy(str,"abc"); //无法修改字符串常量的值
printf("%s",str);
}
有问题,GetMemory函数中的p指针指向的是一个字符串常量,返回后,str也是指向这个字符串常量,是无法修改字符串常量的值。
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str,"Hello World");
printf("%s",str);
}
没有问题,完全正确,因为指针作为函数参数进行传递时,只能改变指针指向的值,而不能改变指针的指向,如果想要改变指针的指向,就要使用二级指针该操作。
void GetMemory(char *p)
{
p=(char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str,"Hello World");
printf("%s",str);
}
有问题,Test函数中str一直都是NULL,并没有为其分配空间,所以无法将字符串拷贝到str指向的内存空间。GetMemory函数中的p指针只是一个临时变量,并不是str指针,并没有修改str指针的指向。(指针的传递只能改变指针指向的值,要改变指针的指向只能用双重指针)
void *pp = malloc(10);
cout << sizeof(pp) << endl; //答案 4
char pString2[100] = "google";
int size3 = sizeof(pString2);//答案100
int size3 = strlen(pString2);//答案6
32
A
1 #include<stdio.h>
2
3 int main(void)
4 {
5 int a = 10, b = 20, c = 30;
6
7 printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2));
8
9 return 0;
10 }
复制代码
A:输出的结果为
110..40..60
函数的参数是从右到左执行的,但是打印是从左到右的
char buff[10];
memset(buff,0,sizeof(buff));
gets(buff);
问题在于gets()函数,这个函数是接收标准输入的一串字符串,并且没有检查字符串缓冲区的大小就
直接拷贝到buff数组中,这可能导致在写入buff内存时溢出,可以使用fgets()函数代替这个函数,
main()
{
char *p1=“name”;
char *p2;
p2=(char*)malloc(20);
memset (p2, 0, 20);
while(*p2++ = *p1++); (地址到空 字符串末端)
printf(“%s”,p2);
}
Answer:empty string. 打印为空
栈:由编译器自动分配、释放。在函数体中定义的变量通常在栈上。
堆:一般由程序员分配释放。用new、malloc等分配内存函数分配得到的就是在堆上。
class A
{
public:
A() { p=this; }
~A() { if(p!=NULL) { delete p; p=NULL; } }
};
A* p;
答: delete 会自动调用析构函数。所以析构中调用 delete 引起了无限递归。
Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:
#define dpS struct s *
typedef struct s * tpS;
以上两种情况的意图都是要定义dpS 和 tpS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么?
这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。
思考下面的例子:
dpS p1,p2;
tpS p3,p4;
第一个扩展为
struct s * p1, p2;
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。
int p = 1234;
printf("%2d\n",p); 输出1234 %1d %2d %3d %4d 都是输出1234
int a=1234; printf("%5d\n",a);
那么输出应该是:
_1234 【这里_表示一个空格】
如果是("%50d\n",a),那么1234之前就有50-4=46个空格。
这样明白了吧
33
左值和右值的理解
A(左边要确切 不能出现临时变量)
个人感觉,左值就是可以提供一个确切的可操作的地址,比如前两个赋值语句,左值都是正确的,因为它提供了一个确定的地址,
变量名其实是一个特殊的对地址的引用。第三条赋值语句错误,左值不正确,计算机计算a+24时会将结果存在某个地址,但是这对于我们是未知的,
正如一个普通的整型常量是不可以作为左值的。那是一个不合法的地址.
int a;
int * p ;
a = 29; //true
*p = 30 ; //true
( a + 24) = 10 ; //false
B
C/C++语言中可以放在赋值符号左边的变量,即具有对应的可以由用户访问的存储单元,并且能够由用户去改变其值的量。左值表示存储在计算机内存的对象,
而不是常量或计算的结果。
或者说左值是代表一个内存地址值,并且通过这个内存地址,就可以对内存进行读并且写(主要是能写)操作;这也就是为什么左值可以被赋值的原因了。相对应的还有右值:当一个符号或者常量放在操作符右边的时候,计算机就读取他们的“右值”,也就是其代表的真实值。简单来说就是,左值相当于地址值,右值相当于数据值。右值指的是引用了一个存储在某个内存地址里的数据。
3 = a;//不合法。
a+b = 4;//不合法。
若函数fun的函数头为:
int fun(int i , int j)
且函数指针变量p指向函数fun的赋值语句是 P=fun;
(以下待定)
(void *)ptr 和 (*(void**))ptr的结果是否相同?其中ptr为同一个指针
(void *)ptr 和 (*(void**))ptr值是相同的
这不是指针函数 这是强制类型转化
第一个没问题 就是把ptr强制转化为指向空类型的指针
第二个(*(void**))ptr
(void**)ptr 先看里面的 这是转化成指向void指针的指针 也就是二级指针
再在前面加上*就是取内容 那么内容也是个指针
这个指针是指向空类型的 所以强制转化后和第一个是一样的
34
A
假定要对类AB定义加号操作符重载成员函数,实现两个AB类对象的加法,并返回相加结果,则该成员函数的声明语句为:(??B??)注意 这里是成员函数
?
A.?AB?operator+(AB?&?a?,?AB?&?b)???
????
B.AB?operator+(AB?&?a)?
C.operator+(AB?a)?????
????????????????
D.AB?&?operator+(?)
如果是友元函数 就选A
B
int?square(volatile?int?*ptr)?
?{?
????????return?*ptr?*?*ptr;?
?}?
?
这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
?int?square(volatile?int?*ptr)?
?{?
????int?a,b;?
????
? a?=?*ptr;?
?????
b?=?*ptr;?
???
?return?a?*?b;?
?
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
?long?squart(volatile?int?*ptr)
??{???
??int?a;???
??a?=?*ptr;????
??return?a?*?a;?
?}
要对绝对地址0×100000赋值,我们可以用 (unsigned int*)0×100000 = 1234; 那么要是想让程序跳转到绝对地址是0×100000去执行,应该怎么做?
答案:*((void (*)( ))0×100000 ) ( );
首先要将0×100000强制转换成函数指针,
即: (void (*)())0×100000 然后再调用它: *((vovd (*)())0×100000)();
用typedef可以看得更直观些: typedef void(*)() voidFuncPtr; *((voidFuncPtr)0×100000)();
对齐方式
使用伪指令#pragma pack (n),编译器将按照n 个字节对齐;
使用伪指令#pragma pack (),取消自定义字节对齐方式。
注意:如果#pragma pack (n)中指定的n 大于结构体中最大成员的size,则其不起作用,结构体仍然按照size 最大的成员进行对界。
另外,通过__attribute((aligned (n)))也可以让所作用的结构体成员对齐在n 字节边界上
野指针”不是NULL指针,是指向“垃圾”内存(不可用内存)的指针。“野指针”是很危险的,if无法判断一个指针是正常指针还是“野指针”。有个良好的编程习惯是避免“野指针”的唯一方法。
野指针的成因主要有三种:
一、指针变量没有被初始化。指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
三、指针操作超越了变量的作用范围。比如不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。比如说某个地址的生命期,使用一个没有生命期的指针是非常危险的。
35
A
虚成员函数不可能是static成员函数
struct MyStruct
{
char dda;
double dda1;
int type;
};
printf("%d\n",sizeof(MyStruct)); 打印出来24
(即结构中占用最大空间的类型所占用的字节数sizeof
(double)=8)的倍数
int *a[2][3];
printf("%d\n",sizeof(a)); 答案 24 (64位系统和32为系统)(有待商榷)
简述Critical Section和Mutex的不同点
答: 对几种同步对象的总结
1.Critical Section A.速度快 B.不能用于不同进程
C.不能进行资源统计(每次只可以有一个线程对共享资源进行存取)
2.Mutex A.速度慢 B.可用于不同进程 C.不能进行资源统计
3.Semaphore A.速度慢 B.可用于不同进程 C.可进行资源统计(可以让一个或超过一个线程对共享资源进行存取)
4.Event A.速度慢 B.可用于不同进程 C.可进行资源统计
realloc函数在使用上要注意什么问题。 这个函数我也才知道的,汗一个。据我的初步理解,这个函数的作用是重新分配空间大小,返回的头指针不变,只是改变空间大小。既然是改变,就有变大、变小和为什么改变的问题。变大,要注意不能大到内存溢出;变小,那变小的那部分空间会被征用,原有数据不再存在;为什么改变,如果是想重新挪作他用,还是先free了吧。
strtok函数在使用上要注意什么问题。 这个问题我不知道能不能回答全面,因为实在是用的很少。这个函数的作用是分割字符串,但是要分割的字符串不能是常量,这是要注意的。比如先定义一个字符串:char array[]="part1,part2";,strtok的原形是char *strtok(char *string, char *delim);,我们将","作为分隔符,先用pt=strtok(array,",");,得到的结果print出来就是"part1",那后面的呢,要写成pt=strtok(NULL,",");,注意,要用NULL,如果被分割的字符串会被分成N段,那从第二次开始就一直要用NULL。总结起来,需要注意的是:被分割的字符串和分隔符都要使用变量;除第一次使用指向字符串的指针外,之后的都要使用NULL;注意使用这个函数的时候千万别把指针跟丢了,不然就全乱了。
int a=2,b=11,c=a+b++/a++;
printf("%d\n",c);答案 7
等价于 c=2+11/2 a++ b++
局部变量能否和全局变量重名?
能,局部会屏蔽全局。要用全局变量,需要使用”::”
多线程和单线程各自分别在什么时候效率更高?
多线程在并发,并且各线程无需访问共享数据情况详细最高
如果多线程过于频繁切换,或共享数据很多情况下,使用单线程较好
三维数组
多个相同的二维数组可以用三维数组表示。或者说三维数组是以二维数组为元素的数组。
比如一个城市有10个学校,每个学校有10个班,每个班有40个学生,那么表示这10个学校的学生的语文成绩的时候,就可以用一个三维数据 a[10][10][40] 来存储。
#include <stdio.h>
int main(void)
{
int row, column, table;
float values[2][3][5] = {
{{1.0, 2.0, 3.0, 4.0, 5.0},
{6.0, 7.0, 8.0, 9.0, 10.0},
{11.0, 12.0, 13.0, 14.0, 15.0}},
{{16.0, 17.0, 18.0, 19.0, 20.0},
{21.0, 22.0, 23.0, 24.0, 25.0},
{26.0, 27.0, 28.0, 29.0, 30.0}}
};
for (row = 0; row < 2; row++)
for (column = 0; column < 3; column++)
for (table = 0; table < 5; table++)
printf("values[%d][%d][%d] = %f\n", row, column, table,
values[row][column][table]);
return 1;
}
在C语言中也有一个库函数—–atexit(),它的作用就是令程序在退出main()函数之后彻底终止之前自动完成的一些特定的工作,atexit()库函数中记录了指向这些完成特定操作函数的指针(在main之后执行)
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息
36
A
在c语言库函数中将一个字符转换成整型的函数是atol()吗,这个函数的原型是什么?
答案:函数名: atol功能: 把字符串转换成长整型数 用 法: long atol(const char *nptr);
long q;
char *str = "432";
q= atol(str);
printf("string = %s integer = %d\n", str, q); // q=432
软件测试都有那些种类?
答案:黑盒:针对系统功能的测试 白合:测试函数功能,各函数接口
volatile关键字详解
就像大家更熟悉的const一样,volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。
第一
编译器优化问题:
volatile的作用是: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.
简单地说就是防止编译器对代码进行优化.比如如下程序:
XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;
对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,则编译器会逐一的进行编译并产生相应的机器代码(产生四条代码).
第二
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份(在本次线程内,当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后再取变量值时,就直接从寄存器中取值;)。
下面是volatile变量的几个例子:
当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
volatile应该解释为“直接存取原始内存地址”比较合适
C++是不是类型安全的?
答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。
int i=7; printf("%d\n", i++ *i++);
printf("%d\n", i);
分别打印 49和 9
"."如果被重载,就不能调用类成员了,因此不能重载。
“::”如果被重载,则没有办法指明名字空间,因此不能重载。
“?:”这个重载没有意义吧?就像if,else,你重载他干什么...,因此也不能重载
不可重载的运算符集合:{.,.*,::,?:}
C++不能重载的运算符有五个
(1) "."(成员访问运算符)
(2)" .*"(成员指针访问运算符)
(3) "::"(域运算符)
(4)"siezof"(长度运算符)
(5) " ?:"(条件运算符)
37
内存溢出和内存泄露有什么区别?
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
strstr()函数用来检索子串在字符串中首次出现的位置,其原型为:
char *strstr( char *str, char * substr );
str为要检索的字符串,substr为要检索的子串。
返回字符串str中第一次出现子串substr的地址;如果没有检索到子串,则返回NULL。
int a,b;
char *str = "a23456789seesee.com";
char *substr = "see";
char *s = strstr(str, substr);
printf("%s\n", s);
a=strlen(str);
b=strlen(s);
printf("%d\n", a-b); // printf("%d\n", s-str); 可以直接地址相减
exit() 函数与 _exit() 函数的最大区别在于exit()函数在调用exit 系统调用前要检查文件的打开情况,把文件缓冲区中的内容写回文件。也就是图中的“清理I/O缓冲”。
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t result;
result = fork();
if(result<0)
perror("fork");
if(result == 0)
{
printf("This is _exit test\n");
printf("This is the content in the buffer000");
_exit(0);
}
else
{
printf("This is exit test\n");
printf("This is the content in the buffer");
exit(0);
}
return 0;
}
子进程中运行_exit(0)并未将Thisis the content in the buffer000 打印出来,而父进程中运行的exit(0)将Thisis the content in the buffer打印出来了。说明,exit(0)会在终止进程前,将缓冲I/O内容清理掉,所以即使printf里面没有 \n也会被打印出来,而_exit(0)是直接终止进程,并未将缓冲I/O内容清理掉,所以不会被打印出来。
38
A
重复问题
位图法 bit-map
题目:给40个不重复的数 没排序的 然后再给一个数 如何快速判断这个数是不是
在中间
用位图/Bitmap的方法,申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。(实现快速查找这个数是否在中间)
排序问题
一个文件中有9亿条不重复的9位整数 对这个文件中的数字进行排序
解答:
采用位图法 9位整数最大是 999999999 由于9亿是不重复的 把这个组成一个数组
让他有 0---999999999(共10亿)个元素下标表示 0 表示无 1 有 需要的内存为 1G/8=120M左右内存 例如读到2412 就把下标为2412的数组位置1 最后把数组位1的下标存入文件 最终得出排序后的内容
B
Top K问题:在海量数据中找出出现次数最多的K个 最大的K 等等
有1亿个浮点数 找出出现次数最多的10000个
解答:
第一:正常排序 复杂度至少为n*lgn
第二:局部淘汰法
用一个容器装1000个 最后复杂度为 n+m*m m为容器的大小
第三:最小堆法
如 创建大小为1000的堆 复杂度m*lgm
取后面的数依次和堆顶比较 再调整
复杂度 n*lgm 空间复杂度为1000的整数倍
第四:分治法
將一亿个数据分成100份 每份100万 每份找出最大的一万个
第五 Hash法
适用范围:快速查找,删除的基本数据结构,通常需要总数据量可以放入内存
hash统计:先对这批海量数据预处理(维护一个Key为Query字串,Value为该Query出现次数的HashTable,即Hashmap(Query,Value),每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可。最终我们在O(N)的时间复杂度内用Hash表完成了统计;
hash_map(统计频率)
如果这一亿个数里面有很多重复的 先通过hash法去重复 再用别的方法
经常的方法:分而治之/Hash映射?+?Hash统计?+?堆/快速/归并排序
39
s = (char*)realloc(s, sizeof(char)*n);
指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
40
13、typedef char* String_t; 和 #define String_d char * 这两句在使用上有什么区别?
答:typedef char* String_t 定义了一个新的类型别名,有类型检查。
而#define String_d char * 只是做了个简单的替换,无类型检查,前者在编译的时候处理,后者在预编译的时候处理。
同时定义多个变量的时候有区别,主要区别在于这种使用方式String_t a,b;
String_d c,d; a,b ,c都是char*类型,而d为char类型
由于typedef还要做类型检查,#define没有,所以typedef比#define安全。。
计算机在内存中存储数据时使用了大、小端模式,请分别写出A=0X123456在不同情况下的首字节是,大端模式:0X12 小端模式:0X56 X86结构的计算机使用 小端 模式。
int c[4][5]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
int (*p)[5];
p=c;
printf("%d\n",*(*(p+2)+3)); //答案14
int a[3][2]={1,2,3,4,5,6,},*p[3];
p[0]=a[1];
printf("%d\n",*(p[0]+1)); //答案 4
int num = 10;
char str[100];
itoa(num, str, 8); //将整数10转换为八进制保存在str字符数组中
printf("%s\n", str);
互换两个变量 a=a+b;b=a-b;a=a-b;
纯虚函数是怎样实现的?在编译原理上讲一下?
答:在类内部添加一个虚拟函数表指针,该指针指向一个虚拟函数表,该虚拟函数表包含了所有的虚拟函数的入口地址,每个类的虚拟函数表都不一样,在运行阶段可以循此脉络找到自己的函数入口。
抽象类为什么不能实例化?
抽象类中的纯虚函数没有具体的实现,所以没办法实例化。
41 复杂指针
A
(讲究数组元素)数组指针 int (*(*func)[5])(int *p);
func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。
总结一下,就是:
func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,返回值为int类型的函数。
B
(讲究返回值)函数指针 int (*(*func)(int *p))[5];
func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组
从里到外
int (*(*func)(int *p))[5];
可以这样分解:
typedef int (*PARA)[5];
typedef PARA (*func)(int *);
int (*(*x)(int *,char *))(int);
:声明了一个函数指针,这个函数接收一个整型指针和一个字符指针作为参数,返回函数指针,这个函数指针指向的函数接收一个整型参数,返回一个整型。
int(*(*p)(void))[10]; // p是一个函数指针,它指向一个函数,这个函数不接收参数,并且返回一个数组指针,这个指针指向10个int的数组。
C
(讲究指针) 指针数组 char * (*pf[3])(char * p);
这是定义一个函数指针数组。
它是一个数组,数组名为pf,数组内存储了3个指向函数的指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。
D
(讲究返回值)指针函数
int *(*p(int))[3];//可以先跳过,不看这个类型,过于复杂 //从P开始,先与()结合,说明P是一个函数,然后进//入()里面,与int结合,说明函数有一个整型变量//参数,然后再与外面的*结合,说明函数返回的是//一个指针,,然后到最外面一层,先与[]结合,说明//返回的指针指向的是一个数组,然后再与*结合,说//明数组里的元素是指针,然后再与int结合,说明指//针指向的内容是整型数据.
所以P是一个参数为一个整数据且返回
一个指向由整型指针变量组成的数组的指针变量
的函数.
P为函数 形参为int
返回值:返回一个指向 指针数组的 指针
42
http://blog.csdn.net/qiuyunping/article/details/7431111
A
例如一个fp为一个函数指针,(fp代表地址)
则(*fp)()为调用fp指向的的函数
将0转换为--指向返回值为空的函数指针
(void(*)())0
用上面的式子代替fp得到(*(void(*)())0)()
代表硬件会调用地址为0处的子程序。
B
typedef void (*func)();//定义了一个指向返回值是void类型的函数指针
(*(func)0)(); //用上面的指针实现强制转换
typedef void (*func)(void);
func表示一个指向函数的指针类型的名字,该指针类型为”指向返回void类型并且无参数的函数的指针“
可以使用函数名对函数指针进行初始化
typedef void (*func)(void);
void myfunc(void);
func pfun = myfunc;/*赋值*/
pfun();/*调用*/
定义一个函数指针类型。
比如你有三个函数:
void hello(void) { printf("你好!"); }
void bye(void) { printf("再见!"); }
void ok(void) { printf("好的!"); }
typdef void (*funcptr)(void);
这样就构造了一个通用的函数
funcptr words[3] = {&hello, &bye, &ok};
funcptr fun = words[2];
(*fun)();//调用函数
43
A
通过地址调用函数的执行(几种方式)
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
typedef void (*fun)();
void p()
{
printf("mmm\n");
}
int main()
{
void (*ptr)();//函数指针
p();
printf("%x\n",p);//打印函数的入口地址
ptr=(void(*)())0x11f11fe;
ptr();
((void(*)())0x11f11fe)();
((fun)0x11f11fe)();
getchar();
getchar();
}
答案
mmm
0x11f11fe
mmm
mmm
mmm
B
通过地址修改变量的值
int x;
int *p;
printf("%x\n",&x);
p=(int *)0x0012ff60;
*p=3;
printf("%d\n",x);
答案 3
C
如何用宏求结构体的内存偏移地址 (考虑字节对其)
#define FIND( struc, e ) (size_t)&(((struc*)0)- >e)
(struc*)0 //表示将常量0强制转化为struc *型指针所指向的地址 是一个地址
((struc*)0)- >e 表示在0地址处的结构体struc的成员e
&(((struc*)0)- >e)//表示取结构体指针(struc*)0的成员e的地址,
因为该结构体的首地址为0,所以其实就是得到了成员e距离结构体首地址的偏移量.
(size_t)//是一种数据类型,为了便于不同系统之间移植而定义的一种无符号型数据,一般为unsigned int
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
#define FIND(type,e ) ((size_t)&((type*)0)->e)
struct student
{
char a;
int b;
char c[30];
int d;
};
int main()
{
printf("%d\n",FIND(student,a));
printf("%d\n",FIND(student,b));
getchar();
getchar();
}
答案 0 4
扩展一下,现在知道这个变量的地址,求结构体的首地址
#include <stdio.h>
#define FIND(struct,pt,e ) ((int)pt-(int)(&(((struct*)0)->e)))
struct student
{
int a;
int b;
char c[30];
int d;
};
int main()
{
struct student std;
int *m = &std.b;
printf("%d\n",m);
printf("%d\n",FIND(student,m,b));
printf("%d\n",&std.a);
}
答案:x+4 x x
E
指针强制转换后与地址进行加法运算
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
struct student
{
long num;
char *name;
short int data;
char ha;
short ba[5];
};//总共大小24 四字节对其
int main()
{
struct student *p;
p=(struct student *)0x1000000;
printf("%d\n",p);
printf("%d\n",p+200);
printf("%d\n",(char *)p+200);
printf("%d\n",(unsigned long *)p+200);
getchar();
getchar();
}
运行答案:
X X+24*200 x+200 x+4*200
44
A
printf()函数返回值为打印字符个数
int i=4322;
printf("%d\n",printf("%d\n",i));
打印结果: 4322 5(代表i有四个字符加上回车共5个)
B
数组下标为负数
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a[5]={1,2,3,4,5};
int *p=&a[4];
for(int i=-4;i<=0;i++)
printf("%d %d\n",p[i],&p[i]);
printf("%d\n",&a[-2]);//当前地址向前移动 2单位(8字节)
getchar();
getchar();
}
答案: 1 X 2 X+4 3 X+8 4 X+12 5 X+16
X-8
C
我们要切记,对指针进行加1操作,得到是下一个元素的地址,而不是原有地址的数值直接加1,这点大家肯定都知道。假设类型为x,则加1后,指针向后移动sizeof(x),移动是以sizeof(x)为单位的
&a上面也说过了,把a看成一个整体,所以&a + 1是a下一个对象的地址,即&a + 1,
int a[5] = {1, 2 ,3 , 4, 5};
int *m = (int *)&a[0];
printf("%d\n", *(m + 1));
int *p = (int *)&a;
printf("%d\n", *(p + 1));
int *q = (int *)(&a + 1);
printf("%d\n", *(q-1));
int *w = (int *)(&a[0] + 1);
printf("%d\n", *(w-1));
答案: 2 2 5 1
D
int *p1 = new int[10];
int *p2 = new int[10]();
第一个动态申请的空间里面的值是随机值,第二个进行了初始化,里面的值为0
函数指针的好处
如果赋了不同的值给函数指针,那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。
函数指针的用途:一旦函数可以通过指针被传递、被记录,这开启了许多应用,特别是下列三者:
1多态(polymorphism):指用一个名字定义不同的函数,这函数执行不同但又类似的操作,从而实现“一个接口,多种方法”。
2、多线程:将函数指针传进负责建立多线程的API中:例如win32的CreateThread(...pF...)。
3。回调:所谓的回调机制就是:当发生某事件时,自动呼叫某段程序代码。事件驱动的系统经常透过函数指针来实现回调机制,例如Win32的WinProc其实就是一种回调,用来处理窗口听讯息。
求取字符串长度,不使用while、for等循环语句和字符串处理函数。
递归思想
int mystrlen(const char *str)
{
if(*str=='\0')
return 0;
else
return 1+mystrlen(str+1);
}
//应该填入!printf("Hello "), 会先打印出Hello ,然后进行if()判断 !printf()就是0,所以不成立只能运行else,接着打印出World
int main(void)
{
if() // !printf("Hello ")
{
printf("Hello ");
}
else
{
printf("World");
}
return 0;
}
45
A
已知道随机函数rand7(),如何构造rand10()函数
rand7() 返回1 2 3 4 5 6 7之间的一个随机数
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
int rand7()
{
return rand()%7+1;
}
int rand10()
{
int x=0;
do
{
x=(rand7()-1)*7+rand7();
}
while (x>40);
return x%10+1;
}
int main()
{
srand(unsigned(time(0)));
for(int i=0;i!=10;i++)
cout<<rand10()<<" ";
cout<<endl;
getchar();
getchar();
}
输出结果 9 7 10 8 7 8 9 4 5 8等等之类的
B
(void *)ptr和(*(void**))ptr的结果是否相同?
其中ptr为同一个指针答案:.(void *)ptr和 (*(void**))ptr值是相同的
这不是指针函数 这是强制类型转化
第一个没问题 就是把ptr强制转化为指向空类型的指针
第二个(*(void**))ptr
(void**)ptr 先看里面的 这是转化成指向void指针的指针 也就是二级指针
再在前面加上*就是取内容 那么内容也是个指针
这个指针是指向空类型的 所以强制转化后和第一个是一样的
C
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a[5]={1,2,3,4,5};
int b[100];
int *p=(int *)(&a+1);//&a 数组指针 a数组首地址
printf("%d %d\n",*(a+1),*(p-1));//2 5
printf("%d\n",sizeof(b));// 400
printf("%d\n",sizeof(&b));// 4
getchar();
getchar();
}
46
A
嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
一个较晦涩的方法是:
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二种方案,但我建议你在面
B
36.?如何打印出当前源文件的文件名以及源文件的当前行号??
答案:
cout?<<?__FILE__??
cout<<__LINE_
__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。
C
?下面的代码输出是什么,为什么
?void?foo(void)??
{?unsigned int a=6;
int b=-20;
(a+b > 6) ? puts("> 6") : puts("<= 6");??
}?
C中运算有规定,如果整型变量间进行数据运算,只要有一个变量是无符号的,结果就按无符号数据输出,因此a+b > a
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是“>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。?因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。
D
嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a
的bit 3。在以上两个操作中,要保持其它位不变。
#define BIT3(0x1<<3)
static int a;
void set_bit3(void)
{
a|=BIT3;
}
void clear_bit3(void)
{
a &=~BIT3;
}
E
字符格式
unsigned int a=0xfffffff7;
unsigned char i=(unsigned char)a;
char *b=(char*)&a;
printf("%08x\n",i); //000000f7
printf("%08x\n",*b);//fffffff7
printf("%d\n",i); //247
printf("%d\n",*b);//-9
unsigned int a=0xfffffff7;
char i=(char)a;
printf("%d\n",i);// -9
47
A
float(**def)[10]
数组指针
def是一个二级指针 他指向一个一个有10个元素的指针 元素为float形
double *(*g)[10]等价于double *((*g)[10])
数组指针
g一个指针 他指向一个一个有10个元素的指针 元素为double * 指针
int (*(*fun)(int,int))(int)
函数指针 指向的函数 形参 为 int int 返回值也是一个函数指针 这个指针形参
为 int 返回值为int
B
int *pa=NULL;
int *pb=pa+15;//pb=0+15*4
printf("%x\n",pb);//%x 16进制 60=3c
C
char * stra()
{
char str[]="hello";
return str;
}//不可以
char * stra()
{
char *str="hello";
return str;
}//可以
char * stra()
{
static char *str="hello";
return str;
}//可以
解释:
char str[]="hello";是分配一个局部数组 内存中的栈 str[0]='t' 可以修改
char *str="hello";是分配一个全局数组 内存中的全局去 *str="rr"错误 不能修改
static char *str="hello";是分配一个全局数组
D
int b=3;
int arr[]={6,7,8,9,10};
int *ptr=arr;
*(ptr++)+=123;
printf("%d %d\n",*ptr,*(++ptr));
答案 8 8
解释: *(ptr++)+=123;等价于 *(ptr)+=123; ptr++;
printf语句执行顺序 从右往左 打印从左往右
E
内连函数
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
inline int f(int i) {return i*i;};
void main()
{
printf("%d\n",f(9));
getchar();
getchar();
}
F
指针强制转换
int a=8;
int b=9;
int *p=&a;
float *d=(float *)&b;
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
int a=3;
void main()
{
int a=10;
::a++;//全局
printf("%d\n",::a); //全局 4
printf("%d\n",a);//局部 10
getchar();
getchar();
}
int a,x;
for(a=0,x=0;a<=1&&!x++;)
a++;
cout<<a<<" "<<x<<endl;// 1 2
int a=10,x=3,z=3;
a==(x=z=3);
printf("%d\n",a);// a=10 a不等于3 仍然为10
48
A
二维数组的地址
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
printf("%d,%d,%d,%d,%d\n",a,*(a+0),a[0],&a[0],&a[0][0]); // X
printf("%d,%d,%d,%d,%d\n",a+1,*(a+1),a[1],&a[1],&a[1][0]);//X+16
printf("%d,%u,%d,%d,%d\n",a+2,*(a+2),a[2],&a[2],&a[2][0]);//X+32
printf("%d,%d\n",a[1]+1,*(a+1)+1); //X+20
printf("%d,%d\n",*(a[1]+1),*(*(a+1)+1)); // 5 5
getchar();
getchar();
}
B
数组指针
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int i,j,(*p)[4];
p=a;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
printf("%d ",*(*p+j));//等价那种 *(*(p+i)+j)
}
p++;
printf("\n");
}
getchar();
getchar();
}
打印
1,3,5,7
9,11,13,15
17,19,21,23
C
指向二维数组的指针变量
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int *p;
int *q;
for(p=a[0];p<a[0]+12;p++)
{
if((p-a[0])%4==0) printf("\n");
printf("%d ",*p);
}
printf("\n");
q=a[0];
q++;
printf("%d\n",q-a[0]);//注意等于1 指针相减为元素的个数
getchar();
getchar();
}
打印:
1,3,5,7
9,11,13,15
17,19,21,23
1
D
双重指针
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int *b[3]={a[0],a[1],a[2]};
int **p=b;
int i,j;
for(i=0;i<3;i++)
for(j=0;j<4;j++)
printf("%d,%d,%d,%d\n",a[i][j],*(*(a+i)+j),*(b[i]+j),*(*(p+i)+j));//打印相同的数字
getchar();
getchar();
}
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
char *ps[3]={"aaa","bbb","ccc"};
char **pp;
pp=ps;
for(int i=0;i<3;i++)
puts(*pp++);
getchar();
getchar();
}
打印结果
aaa
bbb
ccc
char * a[]={"hello","the","word"};
char **p=a;
p++;
cout<<*p<<endl;
打印结果 the
49
A
内部函数和外部函数
内部函数又称静态函数 他只能在定义他的源文件中被调用 而不能被其他源文件中的函数调用 定义内部函数时候 需要在函数首部最左端加关键字static
外部函数 凡是不被说明为内部函数的的函数 均是外部函数 外部函数可以被其他源文件中的函数调用
定义时候可以加extern显示说明 不加也可
内部函数
如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数称为内部函数。
定义一个内部函数,只需在函数类型前再加一个“static”关键字即可,如下所示:
static 函数类型 函数名(函数参数表)
{… …}
关键字“static”,译成中文就是“静态的”,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。
使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。
外部函数
外部函数的定义:在定义函数时,如果没有加关键字“static”,或冠以关键字“extern”,表示此函数是外部函数:
[extern] 函数类型 函数名(函数参数表)
{……}
调用外部函数时,需要对其进行说明:
[extern] 函数类型 函数名(参数类型表)[,函数名2(参数类型表2)……];
例 外部函数应用。
(1)文件mainf.c
main()
{ extern void input(…),process(…),output(…);
input(…); process(…); output(…);
}
(2)文件subf1.c
extern void input(……)
{……}
(3)文件subf2.c
extern void process(……)
{……}
(4)文件subf3.c……
extern void output(……)
{……}
例 数组排序----简单选择排序
file1.c
main()
{
extern void sort(int array[ ],int n);
int a[10],i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
}
file2.c
void sort(int array[],int n)
{ int i,j,k,t;
for(i=0;i<n-1;i++)
{ k=i;
for(j=i+1;j<n;j++)
if(array[j]<array[k]) k=j;
if(k!=i)
{ t=array[i];
array[i]=array[k];
array[k]=t;
}
}
}
B
typedef的使用
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
typedef int COUNT[10];//定义COUNT为int 数组
void main()
{
COUNT a={1,2,3};
printf("%d \n",a[0]);// 1
getchar();
getchar();
}
typedef和define的区别
typedef int * STR;
int a=3,b=8;
STR c ,d;
c=&a; d=&b;
printf("%d \n",*c);
printf("%d \n",*d);
#define STR int*
int a=3,b=8;// STR c,d;是错误的 因为只能代表C为指针 D不是
STR c ;
STR d;
c=&a; d=&b;
printf("%d \n",*c);
printf("%d \n",*d);
C
迷途(stray)指针,也被称为野(wild)指针或悬浮(dangling)指针,是指将delete 用于指针(从而释放它指向的内存),但没有将它设置为空时引发。如果随后你在没有重新赋值的情况下使用该指针,后果将是不可预料的:程序崩溃算你走运。
通常delete一个指针后 再对这个指针PTR=0;这样可以赋为空指针,这样就消除迷途指针带来的危害
“野指针”产生原因及解决办法如下:
?
(1)指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以是具体的地址值,也可让它指向NULL。?
(2)指针?p?被?free?或者?delete?之后,没有置为?NULL。解决办法:指针指向的内存空间被释放后指针应该指向NULL。
?
(3)指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间并且让指针指向NULL。
int *ptr;
ptr=(int *)0x8000;
*ptr=3;
解答:指针问题,首先将指针ptr指向一个指定的地址,即对一个未作声明的地址直接进行访问,所以访问出错。这块地址可能已经被用,或者被其他程序占用等,因此会导致错误。
50
A
Register变量:动态和静态变量都是存放在内存中,程序中遇到该值时用控制器发指令将变量的值送到运算器中,需要存数再保存到内存中。如果频繁使用一个变量,比如一个函数体内的多次循环每次都引用该局部变量,我们则可以把局部变量的值放到CPU的寄存器中,叫寄存器变量。不需要多次到内存中存取提高效率。
寄存器变量没有地址 若n为寄存器变量 以&n引用n是错误的表示形式
一个计算机中寄存器数量有限 不能任意定义多个寄存器变量
只有局部自动变量和形参可以定义为寄存器变量
语言中包括了关键字auto,它可用于定义局部变量。但自从所有的非全局变量的缺省值假定为auto以来,auto就几乎很少使用了。 在C或者以前的C++中,auto关键字基本上可以被无视:比如这个局部变量:int a = 100;auto int a = 100;并没有什么区别。
函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。
B
静态全局变量
static全局变量与普通的全局变量有什么区别 ?
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。
全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。
这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
static全局变量只初使化一次,防止在其他文件单元中被引用;
注意:全局变量和全局静态变量的区别
1)全局变量是不显式用static修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。
2)全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。
静态局部变量
只初始化一次 这次保留上次的值使用
C
局部变量和全局变量
例如文件a.c需要引用b.c中的变量int v,就可以在a.c中申明extern int v;这样就可以使用
a.h b.c c.c 其中a.h分别被包含在 b.c和c.c中 如果要定义一个在b c都能使用的变量 若在a.h中定义是不行的(出现重复定义错误) 只能在其中一个C文件中定义 另外一个用extern引用
51
C语言文件操作
A
给桌面一个文件写入消息
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
FILE *fp;
char ch,filename[10];
if((fp=fopen("D:\\Users\\litingting\\Desktop\\aaa.txt","w"))==NULL)
{
printf("error");
exit(0);
}
for(;(ch=getchar())!='@';)
fputc(ch,fp);
fclose(fp);
getchar();
getchar();
}
读消息
#include "stdafx.h"
#include "string.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
void main()
{
FILE *fp;
char ch,filename[10];
if((fp=fopen("D:\\Users\\litingting\\Desktop\\aaa.txt","r"))==NULL)
{
printf("error");
exit(0);
}
for(;(ch=fgetc(fp))!=EOF;)//每次读取一个字符存入ch
putchar(ch);
putchar('\n');
fclose(fp);
getchar();
getchar();
}
52
A
C/C++语言中的main函数,经常带有参数argc,argv,如下:
int main(int argc, char** argv)
这两个参数的作用是什么呢?argc 是指命令行输入参数的个数,argv存储了所有的命令行参数。假如你的程序是hello.exe,如果在命令行运行该程序,(首先应该在命令行下用 cd 命令进入到 hello.exe 文件所在目录)
运行命令为:hello.exe Shiqi Yu
那么,argc的值是 3,
argv[0]是"hello.exe",argv[1]是"Shiqi",argv[2]是"Yu"。
B