存档

‘C/C++’ 分类的存档

虚函数(多态)& 虚继承

2012年5月23日 2 条评论

---

熟悉多态的都知道,虚函数的动态绑定是通过一张虚函数表来实现的,编译器在对象内存中维护一个指向虚函数表(vtable)地址的指针vptr。

对于虚继承,编译器需要在对象内存中维护一个虚拟指针指向父类,虚继承是为了解决多重继承中的二义性问题。

关于虚函数表以及各种情况下对象的内存分布,已经有很好的博客做了分析,相关知识可以参见陈皓博客:

C++虚函数表解析:http://blog.csdn.net/haoel/article/details/1948051

C++对象内存布局(上):http://blog.csdn.net/haoel/article/details/3081328

C++对象内存布局(下):http://blog.csdn.net/haoel/article/details/3081385

本文只是在看了上面几篇博客后写的一道小题来测试验证,以便加深印象。

测试分析了虚函数的单继承和多继承情况、虚继承的单继承和多继承情况、虚函数和虚继承同时出现的情况。代码如下:

阅读全文...

成员在类中的偏移量 & 类成员指针

2012年5月18日 没有评论

---

看一道笔试题(引自程序员面试宝典):写出程序输出结果

#include <stdio.h>

class A
{
public:
	A() {m_a = 1; m_b = 2;}
	~A() {}
	void fun() {printf("%d %d", m_a, m_b);}
private:
	int m_a;
	int m_b;
};

class B
{
public:
	B() {m_c = 3;}
	~B() {}
	void fun() {printf("%d", m_c);}
private:
	int m_c;
};

void main()
{
	A a;
	B *pb = (B*)(&a);
	pb->fun();
}

程序的输出结果为1。

暂且不讨论该程序设计有多么糟糕,但程序主要考察关于类对象成员调用的机制,关于这方面,据说在《深入理解C++对象模型》中有详解,我没有做深入研究,只是根据看《primer》和《C++必知必会》中的一些知识说一下自己的理解。

这里主要涉及到两方面:一是对象调用成员函数时会将调用对象与函数绑定;二是对象访问成员是根据该成员距离对象的偏移量来访问的,而不是根据成员名来访问,所谓偏移量,就是告诉你一个特定的成员位置距离对象的起点有多少个字节。

阅读全文...

从《The C Programming Language》中学到的那些编程风格和设计思想

2012年3月20日 2 条评论

---

读书不是目的,关键在于思考。

很早就在水木上看到有人推荐《The C Programming Language》这本书,一直都没看,开学一个月就专心拜读了一下,并认真做了课后习题。读来收获不少,主要有两点:一是加深了自己对一些基础知识的理解和感悟;二是从中学到了一些不错的编程风格和设计思想,这些东西虽看起来不起眼但细细嚼来还是很值得学习的。下面就从四个方面做一个小总结,水平有限,加之刚读第一遍,难免有疏漏和错误,非常欢迎批评补充。

===读书感悟===

===设计思想===

===编程风格===

===经典例程===

读书感悟

首先,不得不说这不愧为大师之作(网上将其誉为C圣经),一本薄的不能再薄的书,200多页,却涵盖了C语言的大部分精粹;值得一提的是,该书仅仅定价30元,这在计算机类书籍中可以说是很便宜了,与市面上充斥的各种C语言教程在性价比上形成了很大的对比。不过不得不承认,个人感觉这不是一本入门书,读起来是需要一定基础的。

其次,说一下该书的撰写风格和读书建议。书中不是成篇幅地罗列一个个语法和知识点,而是以例程驱动,大部分知识点都是以一个小程序来说明,所以建议读的过程中也将例程当做习题来做,然后与作者给出的程序进行对比,会发现自己的思维是多么的不缜密,考虑问题是多么的欠缺,等你慢慢“上道”了,写出一个和作者类似甚至觉得比他给出的答案要好的时候,兴趣和成就感便会促使你良性循环。

也许有人会说,书中的例子很简单,但我想说尽管很简单,但每个例子都是经典之作,而且能有效地治理眼高手低,在编写代码的过程中,太多的细节让人醍醐灌顶,可谓处处珠玑,书中一些精巧的程序段不禁会让人感觉:啊哈,原来是这样,原来还可以这样写。这样读来能明白之前好多不知所以然的地方。

最后,说一下这一本不到300页的书都包含了什么内容。书中从经典的hello world开始,可以说是手把手编写并讲述了C语言的大部分语法,不仅如此,更实现了二分查找快排希尔排序(这个的实现比我们数据结构中学习的要巧妙不少)、链表二叉树哈希这些重要的数据结构和算法。书中的大部分例程不仅能让你了解C,不仅能教你如何编写有效率且易读的代码,更能让你了解一些底层的设计思想,例如getchar,strcpy,fopen,printf等众多库函数的实现思想都有体现,帮助你探索源码,追根溯源。另外书中还包含了一些系统调用接口,编译原理(一个递归下降的语法分析,这部分没看懂,还要再读啊)的实现等。

阅读全文...

数组、指针和地址运算:一个经典的小问题

2012年3月18日 没有评论

---

在《The C Programming Language》中提到,数组、指针和地址的算术运算是C语言的一大优点,使用指针运算不仅能使得代码简洁精炼,更能编写高效率的程序,但是灵活运用却不是那么容易的事情,在使用过程中可能会遇到很多小问题,本节罗列一个比较经典的小例子。

代码见下:

#include<stdio.h>

void test(char *arg[]);

void main()
{
	char *arg[] =
	{
		"find",
		"-x",
		"-n"
	};
  //printf("%c\n",(*++arg)[0]); // --1
	test(arg);
	printf("%c\n",*++arg[0]); // --2
}

void test(char *arg[])// --3
{
	printf("%c\n",(*++arg)[0]);
}

对于上述代码分析如下:

阅读全文...

动态二维数组的申请与释放:使用指针数组和数组指针在物理空间上的区别

2012年3月18日 没有评论

---

上一篇文章中,我们提到使用数组指针申请动态二维数组时的释放问题,在C/C++中,动态申请二维数组有两种方式,一种是先申请一个指向所需类型的指针数组,然后对数组中的每个元素再依次申请空间,在物理形态上该方法申请了两次空间;另一种是使用数组指针直接申请所需的空间,物理形态上该方法只申请了一次空间。

二者在内存的物理空间上是具有不同的表现形式的,如图所示,本节我们就简单讨论下。

两种方法皆见代码,为了对比,我们也定义了一个一维数组;为了验证上面所说的二者在物理空间上的形态差别,我们分别输出了一系列变量。

阅读全文...

C/C++内存泄漏检测 & 数组指针的空间释放

2012年3月18日 1 条评论

---

周一助教上机课,有学生问到动态申请的二维数组空间如何释放,对于使用二维指针申请的只要一步步释放掉就可以了,让人可能产生疑惑的就是使用数组指针申请的情况,即

int (*p)[3] = new int [4][3];
// ...
delete []p;    //---1
delete[](*p);  //---2

在释放这个二维数组时,应该使用1和2哪种方式呢?哪种对呢?

其实静心分析下,并不难,哪种写法都是对的。

为什么?

首先我对于每种释放情况都测试内存是否有泄漏,以此验证释放的正确性;然后我们使用最直接的方法输出相关数据来验证释放的正确性。

对于C/C++如何检测内存泄露,就是简单的库调用,这里不赘述,详见下面代码和参考文献,不过注意,检测结果的信息输出是在调试的情况下查看输出窗口(output)的

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#include <stdio.h>

void main()
{
	int (*p)[3] = new int [4][3];

	//delete []p;
	delete[](*p);

	_CrtDumpMemoryLeaks();//调试运行到该步,输出检测信息
}

阅读全文...

volatile关键字

2012年3月18日 2 条评论

---

The C Programming Language》中提到volatile关键字,没怎么看明白,就查了一点资料,另外,实验室的“万古萌物”童鞋给我提供了一个小例子,如下两张图片很好地说明了volatile关键字的用途。

volatile关键字在嵌入式中用的比较多,同const一样都是类型限定符。

volatile关键字在当一个变量有可能意外地发生变化(如多线程应用中被几个任务共享的变量)的情况下使用。volatile意在阻止编译器对该变量进行优化,防止因其意外改变而发生错误。即,在用到这个变量时,都会从新从该变量的原始地址读取变量值,而不是直接读取保存在寄存器中的值

    

阅读全文...

分类: C/C++ 标签: ,

二进制思考系列文章(目录)

2012年3月2日 2 条评论

二进制思考(四):位运算经典题目练习

2012年3月2日 没有评论

---

本文主要是基于《The C Programming Language》的位运算题目进行了一个整理,第一道题和第四道题都是该书中的题目,本节在这里给出一些可能的实现。

========目录=======

--二进制1的个数

--前导0的个数

--二进制逆序

--二进制循环移位

--高低位交换

==================

二进制1的个数

计算二进制1的个数,位运算可以有多种方法,本文实现了三种方法,前两种思想基本一样,细节不同,可对比看一下,欢迎讨论,代码如下

阅读全文...