今天看到了TIOBE编程语言社区排行榜9月的数据,从第一到第四分别是:c、java、objective-c、c++。其中java和c++同比都下降了几个百分点!objective-c似乎乘着swift的高风,一路狂飙!在前十的名单中swift也疯狂的杀了进来,势头之猛,不可小觑!自我感觉还是那个观点:基础不好,上层一切都是浮云!java的JVM是c++开发的;objective-c底层是c;swift又是objective-c构建出来的……一切的本源其实都离不开c与c++,甚至是汇编!加速、扎实的补好基础,才是王道!
今天来总结梳理,在面向过程当中最为重要的两章:数组与指针。可以这么说吧,这两个东西在c与c++的语言中,是灵魂没有之一!难度与重要性不言而喻。java的最初开发者就是一名c++程序员,因为被c++的繁琐的指针与类与类多重继承二义性等一系列的东西所烦恼,故一怒之下开发了java(大牛啊!)。在速度与激情的当今,效率、内存、算法等等一系列的计算机关键词环绕于我们身边,在此种情况下,我们更要加强在加强对指针的掌握,才能在技术领域处于不败之地。
Chapter5~chapter6
->数组
1、数组定义的过程中,例如:a[n],其中[]中的变量n必须是常变量,例如:const int n=5,否则非法。或者我们可以动态的申请数组空间,但是动态申请是在堆上申请,例如:int *a = new int[n],在此种情况下,n可以为变量。
#include<iostream> using namespace std; int main(){ int n; cin >> n; int *a = new int[n]; for (int *p = a, i = 0; i < n; i++){ *(p + i) = i; } for (int *p = a, i = 0; i < n; i++){ cout << *(p + i) << endl; } return 0; }
2、这种情况:int a[3]={1,2},将前两个数组位赋值成1和2,后面一个位置,默认初始化,值为0;char类型的数组,默认初始化为'\0'。
3、经典的冒泡排序!(必须手到擒来,信手拈来。否则还学什么计算机!!)
/* 冒泡排序 */ #include<iostream> using namespace std; void bubble(int a[],int n){ if (n == 0) return; else{ for (int i = 0; i < n; i++){ for (int j = 0; j < n -1 - i; j++){//自己感觉在这边界的问题上有点小纠结下! if (a[j]>a[j + 1]){ int t = a[j + 1]; a[j + 1] = a[j]; a[j] = t; } } } } } int main(){ int a[] = { 4, 2, 5, 1, 6, 2, 6 }; bubble(a, sizeof(a) / sizeof(int)); for (int i = 0; i < sizeof(a) / sizeof(int); i++){ cout << a[i] << " "; } cout << endl; return 0; }
4、二维数组表达的几种方式,其中包含了默认值的问题:
(1)int a[2][3]={{2,3,4},{5,6,7}};
(2)int a[][3]={3,4,5,6,4,43,3}(第二维度数值不能省略)
(3)int a[2][3]={{},{34,45}}
5、数组作为函数的参数的时候,例如:void function(int a[]),其中传进来的实参,是一种值传递,但是只是将数组元素的首元素的地址传递给了形参而已。所以在函数内部接收到的数组并不能运用于一些STL中对数组操作的函数,例如bigin()与end()等(这个似乎是c++11新添加进来的函数)。
6、二维数组用成函数参数的时候,一维的数值可以省略,二维的数值必须要给出,而且要和实参的一样!
7、字符串数组的一个自己测试的问题:
char a[5] = { '1', '2', '3', '4','\0' };
这种情况下,如果最后的'\0'字符换成其他的字符,不空缺,那样我用cout<<a来输出,编译器会将a当成字符串来处理,而没有结束符,会多输出一些随机的字符在后面,所以在实际的使用过程当中,我们会人为的将这种形式的字符数组,在最后加上一个'\0'。
8、原c语言中,对于字符数组的几种操作函数:(包含在#include<cstring>中)
strcat(char[],const char[]):字符串连接函数
strcpy(char[],const char[]):字符串复制函数,第一个数组空间必须要大于第二个字符串的大小+1,否则报错
strcmp(const char[],const char[]):字符串比较函数
strlen(const char[]):求字符串的长度函数
9、c++中关键的字符串处理类型:(string类)
任何语言,对于字符串处理都是非常关键的。以前的c用的是字符数组来处理,并不是那么的方便与安全。在c++的领域,我们看到了string类型的诞生,是非常让人欣慰的!(装逼了~哈哈!)总结了下,有如下的几种改进。
(1)定义字符串变量可以直接用等于号,例如:string str = "jicheng";
(2)字符串常量是以'\0'结尾的,编译器会自动加上。当把字符串常量赋给string类型的变量的时候,并不包括最后的那个'\0'。
(3)string类型的变量,可以直接使用复制运算,例如:string str = "jicheng",也可以直接使用各种关系运算符,例如:==、>、>=、<、<=、!=等,这些都在string类里面进行了运算符的重载!比操作字符数组要方便多了。
10、习题1:
用筛选法求n以内的素数。(这个筛选法我在网上查了下,看到了几种优化的算法实现。在以前的那种求素数的算法中是看原数能不能被2到sqrt(n)之间的数整除,如果能就不是素数,时间复杂度为O(n*sqrt(n)),相对来说数量级是比较大的。筛选法就是为了处理较大的n而出现的)
/* 筛选法的素数求法。我在网上查了下,发现如下的一种高效算法,主要思想就是:我只判断奇数是不是素数,因为偶数肯定不是素数(2除外),所以我们申请一个布尔数组来对应相应的奇数,例如布尔数组下标为0,对应奇数是3,下标为1,对应奇数是5,对应公式为:num=i*2+3,在如下的应对关系下,将每个布尔数组都赋值为true。从第一个开始,如果后面的值是true的就是素数,筛选掉的基本方法就是,每个下标对应的数的倍数,例如3是第一个数组下标1对应的数,3+3=6就不是素数,要筛选掉,可是6本身是偶数不在我们所考虑的对应关系内,故继续查找;不难看出,3+3+3=9也要筛选掉,并且它是奇数,使我们要考虑的对应关系,所以通过9来求得具体的布尔下标((num-3)/2),将此下标对应的布尔值设为false。就是这样。最后输出时,遍历布尔数组,如果是true的,输出i*2+3,即可! */ #include<iostream> #include<cmath> using namespace std; int main(){ int n; cout << "请输入您要筛选的n!" << endl; cin >> n; int size = (n % 2 == 0) ? (n / 2) : (n / 2 + 1);//此处的作用是获得我要申请与之对应的布尔数组的大小,因为要对应的是奇数,所以当输入是偶数是直接就是原数的一半,如果是奇数的话,还要加一 bool *bl = new bool[size - 1];//我们要将1去除,直接从3开始,所以申请的对应布尔数组要size-1 for (int i = 0; i < size - 1; i++){ bl[i] = true; } for (int i = 0; (i*2+3)<=sqrt(n); i++){ if (!bl[i]) continue; int j = i*(2 * i + 6) + 3;//网上的高效算法核心,就是当筛选到一个数的时候,我们可以得到第一个之前没有筛选掉的那个数,就用这个公式求得这个数的下标,我还是不是很能理解这个公式的推算过程。 while (j < size - 1){ bl[j] = false; int num=j*2+3+i*2+3;//此处是获得下一个数 while (num % 2 == 0) {//当下一个数是偶数的时候,因为我们假设的bl对应的是从3开始的奇数,所以当碰到偶数的时候我们要跳过 num = num + i * 2 + 3;//再加一次开始的那个素数,直到获得的是奇数才结束。 } j = (num - 3) / 2;//此时求得下一个要筛选的下标 } } cout << 2 << endl; for (int i = 0; i < size - 1; i++){ if (bl[i]){ cout << (i*2+3) << endl; } } return 0; }
11、习题6:
打印杨辉三角前十行。
/* 打印杨辉三角(10行)。这个锻炼的是二维数组的基础能力 */ #include<iostream> #include<cmath> using namespace std; int main(){ int a[10][10]; for (int i = 0; i < 10; i++){ for (int j = 0; j < 10; j++){ if (j == 0 || i == j) a[i][j] = 1; else if (j>i) a[i][j] = 0; else a[i][j] = a[i - 1][j] + a[i-1][j - 1]; } } for (int i = 0; i < 10; i++){ for (int j = 0; j < 10; j++){ if (a[i][j]!=0) cout << a[i][j] << " "; } cout << endl; } return 0; }
12、习题11:
打印图案(用string方法,这个能用上标准输出格式化的函数!)
/* 标准输出格式化控制 */ #include<iostream> #include<iomanip> #include<string> using namespace std; int main(){ string str = "* * * * * *"; for (int i = 0; i < 5; i++){ cout << setw(str.size() + i) << setiosflags(ios::right) << str << endl; } return 0; }
->指针
1、指针的自我多年体会:
指针,指了个针,指TM个针,这东西让人又爱又恨啊。恨在他的复杂多变易出错,爱在他的灵活可控变化多。自从研究生入了java的门之后,发现很多时候,还是怀念当初的c与c++的日子。虽然抓耳挠腮,但是,过后的领悟与收获,是远远大过我们去灵活使用一个框架的。指针,其实就是地址,就是原存储数据的地址,当然这个地址也可以存在另外一个区域,所以就有地址的地址的这一表现形式,例如:int *p = 4,其中p就是数据4的地址,*p就是指向了数据4的存储空间;int **p = 4,这个就是地址的地址,其中p是地址,*p指向了*p的地址,**p指向了4的存储空间。编译器将地址设置为一个整型,就是一个地址占4字节的存储空间,例如上面的p。
2、“调用函数时不会改变实参指针变量的值,但可以改变实参指针变量所指向变量的值”,这句话的理解就是那种值传递的问题。因为我传进来的是指针,例如:function(*p),其中p的值是地址,所以我们在函数内部是进行了一个副本拷贝的,编译器自己默认完成,拷贝的的就是p的地址,在此我们假设为_p,其中*_p和*p一样,都指向了实参原指向的数据空间,所以我们在函数内部去改变指向的数据空间的值,就是改变原实参的指向的数据空间的值;可是我们去改变指针变量的值,例如:p=&a,这样其实是改变的_p这个副本的值,并没有改变原p变量的值,所以有了上面那句话。
3、指针的各种模式:
(1)int *p;//普通的指针变量
(2)int a[n];//数组变量,其中a指向数组第一个元素的地址
(3)int *p[n];//这个是数组指针,数组中大小为n,每个元素都指向了一个整型数据
(4)int (*p)[n];//这个是指向数组的指针,p指向了一个包含有n个整型数据的数组
(5)int f();//普通函数声明
(6)int *p();//返回值是一个指向整型数据的指针的函数
(7)int (*p)();//这个很关键,是指向函数的指针,调用模式可以是:p()
(8)int **p;//指针的指针,这个可以表现为二维数组形式,例如:*(*(p+3)+9)对应于a[3][9]
p.s:这里补充下函数指针:(函数指针用处主要在于一种通用性的方面,例如类似的功能,每个功能的参数和返回类型是一样的,函数名不一样,我们就可以用一个函数指针,分别指向不同的函数进行调用。)
/* 函数指针基础示例 */ #include<iostream> using namespace std; int max(int a, int b){ return a > b ? a : b; } int main(){ int a, b,re; cin >> a >> b; int (*p)(int, int);//定义函数指针,模式是:函数类型 (*指针变量名)(函数形参表) p = max;//将函数指针初始化,其中函数原函数名就是函数的入口地址 re = p(a, b);//函数调用可以直接运用函数指针来调用 cout << re << endl; return 0; }
4、强大的引用的引入(c++很给力的地方之一!):
例如:int &a = b,引用就是变量的别名,所用的存储空间完全一样,上面的a与b所用的地址完全一样,只不过将b变量再起另外一个名字罢了!引用使用的时候要注意的是,在引用声明的时候必须初始化,作为函数的形参又不一样。这里我多说一句,一用用的是&符号,很容易和取地址操作混淆,取地址也是这个符号,具体是什么操作要根据具体的上下文才能分得清,但是当年单纯的我就是认为引用就是取地址,导致只要看到&就各种在那抠,看看到底是什么地址,熟知从一开始的认知就有错了,后面简直寸步难行!希望引以为戒,在将来的学习中,必定要搞清楚搞准确基础的概念,防止事倍功半!下面我就完整的写出c++中两种函数传递的基础模型代码,这两种传递分别是:值传递与地址传递。
/* 两种地址传递模型:值传递与地址传递;其中其实指针的传递就是值传递,在函数内部要进行拷贝复制,只不过和单纯传递变量不同的是,指针传递,传递的是地址罢了。真正的地址传递要用c++的引用,内部传进来的变量地址和外面实参的变量地址是完全一样的!下面我分别写出这两个函数。*/ #include<iostream> using namespace std; void swap(int *a, int *b){//指针的值传递 cout << "a的地址是:"<<&a << endl; if (a == NULL || b == NULL) return; else{ int p = *a; *a = *b; *b = p; } } void swap(int &a, int &b){//引用的地址传递 cout << "a的地址是:" << &a << endl; int p = a; a = b; b = p; } int main(){ int a = 4, b = 8; cout << "交换前:" << a <<","<< b << endl; cout << "a的地址是:" << &a << endl; swap(a, b); cout <<"交换后:"<< a <<","<< b << endl; return 0; }
5、习题10:
讲一个5*5的矩阵中的最小的四个数按照左上、右上、左下、右下的顺序存放,中间存放最大的数字。
/* 习题10 */ #include<iostream> using namespace std; void swap(int &a, int &b){ int t = a; a = b; b = t; } void function(int (*a)[5], int r, int c){ int (*p)[5] = a; for (int i = 0; i < r; i++){ for (int j = 0; j < c; j++){ if (p[i][j] < p[0][0]){ swap(p[i][j], p[0][0]); } else if (p[i][j] < p[0][c - 1]){ swap(p[i][j], p[0][c - 1]); } else if (p[i][j]<p[r-1][0]){ swap(p[i][j], p[r - 1][0]); } else if(p[i][j]<p[r-1][c-1]){ swap(p[i][j], p[r - 1][c - 1]); } if (p[i][j]>a[2][2]){ swap(p[i][j], a[2][2]); } } } } int main(){ int a[5][5] = { {3,4,5,5,6}, {3,6,3,1,6}, {0,8,7,5,3}, {3,5,6,7,8}, {1,2,3,4,6} }; cout << "交换前:" << endl; for (int i = 0; i < 5; i++){ for (int j = 0; j < 5; j++){ cout << a[i][j] << " "; } cout << endl; } function(a, (sizeof(a) / sizeof(a[0])), (sizeof(a[0]) / sizeof(int)));//此处一定要注意传参的问题,因为a[][]这种形式是在栈上申请的空间,所以a代表的是(*a)[5]的地址,和**a所代表的地址并不一样! cout << "交换后:" << endl; for (int i = 0; i < 5; i++){ for (int j = 0; j < 5; j++){ cout << a[i][j] << " "; } cout << endl; } return 0; }
6、习题17:
写strcmp代码。
/* 习题17:据说是微软的源码 */ #include<iostream> using namespace std; int strcmp(char *c1, char *c2){ int ret = 0; while (!(ret = *(unsigned char *)c1 - *(unsigned char *)c2) && *c2)//这里是核心,因为在c++中,非零的数值进行逻辑判断的时候都当成的true的布尔值来处理,就是无论当前两个字符相减是正数还是负数,判断结果都是真,故继续循环,直到结果为0的时候。 { ++c1; ++c2; } if (ret < 0) ret = -1; else if (ret > 0) ret = 1; return(ret); } int main(){ cout << strcmp("ji","ji")<< endl; return 0; }
相关推荐
谭浩强版《C++程序设计》课件是对教材内容的一种详细梳理与总结,它覆盖了从C++的基础语法到面向对象编程的核心概念。下面将根据课件的目录,对各章节涉及的关键知识点进行详细的解析。 #### 第1篇 基本知识 **第1...
以上是《谭浩强C++程序设计》第二章中关于数据类型和表达式的详细知识点梳理,涵盖了整型常量、浮点数、字符常量、符号常量、变量定义及初始化、算术运算符等多个方面。通过这些知识点的学习,读者可以更好地理解C++...
通过以上章节的知识点梳理,我们可以看出《C++程序设计谭浩强100%完整·清华大学》这本书系统全面地覆盖了C++语言的基础知识、面向过程编程以及面向对象编程的核心概念和技术。对于初学者来说,这是一本非常适合入门...
### C++程序设计谭浩强版 #### 一、引言 ...通过上述对《C++程序设计谭浩强版》各章节内容的梳理,我们可以看出这本书覆盖了C++编程从基础知识到高级特性的各个方面,非常适合初学者系统学习C++编程。
### 谭浩强《C++程序设计》内容概览 #### 第1篇 基本知识 第1章 C++的初步知识 - 1.1 从C到C++:介绍了C++语言的发展历程,如何从C语言演化而来。 - 1.2 最简单的C++程序:展示了最基本的C++程序结构。 - 1.3 C++...
### 谭浩强C语言程序设计知识点梳理 #### C语言概述 - **发展过程**:C语言由Dennis Ritchie于1972年在贝尔实验室开发完成,最初是为了移植Unix操作系统而设计的。 - **当代最优秀的程序设计语言**:C语言因其简洁...
通过上述知识点的梳理,我们可以看到谭浩强老师编著的《C++程序设计》这本书内容丰富,覆盖了C++语言的基础知识、面向过程的程序设计、面向对象的程序设计等多个方面,适合初学者系统学习C++语言。
- **当代最优秀的程序设计语言**:C语言因其简洁、灵活和强大,被广泛认为是学习编程的基础。它不仅适用于系统级编程(如操作系统和嵌入式系统),也用于应用软件开发。 - **C语言版本**:C语言经历了多个版本的发展...
### C语言程序设计知识点梳理 #### 一、C语言概述 **1.1 C程序结构特点** - **函数**: C程序的基本单位是函数。每个函数都有自己的功能,并且可以独立编译。 - **函数首部**: 包括函数类型、函数名以及参数列表...
《谭浩强C程序设计课件》是一套全面讲解C语言编程基础的教育资源,主要针对第三版的内容进行了详细的梳理和解读。这套课件涵盖了C语言的14个关键章节,旨在帮助初学者深入理解C语言的基本概念、语法结构以及编程技巧...
### C程序设计基础知识点梳理 #### 一、C语言简介 - **定义**: C语言是一种通用的、过程式的计算机程序设计语言,具有高效、灵活的特点。 - **地位**: 国际上最著名的高级程序设计语言之一,使用范围广泛。 - **...
- **面向对象的程序设计语言**:虽然C语言本身不支持面向对象编程,但其衍生的C++语言支持此类编程范式。 - **C和C++的区别**:C++是基于C语言扩展而成的一种面向对象的编程语言,增加了类、对象等概念。 - **简单的...