C/C++基本语法,入门及提高(1)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ShareUs/article/details/72678165

-- 学习C++可分为4个层次:
  第一层次,C++基础:挑选一本入门书籍,如《C++ Primer》、《C++大学教程》、或Stroustrup撰写的经典《C++程序设计语言》或他一年半前的新作《C++程序设计原理与实践》,而一般C++课程也止于此,另外《C++ 标准程序库》及《The C++ Standard Library Extensions》可供参考;
  第二层次,正确高效地使用C++:此层次开始必须自修,阅读过《(More)Effective C++》、《(More)Exceptional C++》、《Effective STL》及《C++编程规范》等,才适宜踏入专业C++开发之路;
  第三层次,深入了解C++:关于全局问题可读《深入探索C++对象模型》、《Imperfect C++》、《C++沉思录》、《STL源码剖析》,要挑战智商,可看关于模版及模版元编程的书籍如《C++ Templates》、《C++设计新思维》、《C++模版元编程》;
  第四层次,研究C++:阅读《C++语言的设计和演化》、《编程的本质》(含STL设计背后的数学根基)、C++标准文件《ISO/IEC 14882:2003》、C++标准委员会的提案书和报告书、关于C++的学术文献。
  读透《c++ primer》,啃一遍《linux内核源码整体》?

  Java和C++的一个不同点是, 在Java中不可能直接操作对象本身,所有的对象都由一个引用指向,必须通过这个引用才能访问对象本身,包括获取成员变量的值,改变对象的成员变量,调用对象的方法等。而在C++中存在引用,对象和指针三个东西,这三个东西都可以访问对象。其实,Java中的引用和C++中的指针在概念上是相似的,他们都是存放的对象在内存中的地址值,只是在Java中,引用丧失了部分灵活性,比如Java中的引用不能像C++中的指针那样进行加减运算。 

C++知识框架体系图- http://blog.csdn.net/payshent/article/details/55254574
C++入门简单实例- http://blog.csdn.net/aheroofeast/article/details/6856943
c++项目(图书馆管理系统,详细简单适合初学者)- http://download.csdn.net/detail/u011706736/7998273
C入门之简单的学生管理系统- http://blog.csdn.net/way_ping_li/article/details/8040959
链式栈的实现(C实现)- http://blog.csdn.net/gjggj/article/details/72938440
C-Free 5 免安装注册版- http://download.csdn.net/download/u4110122855/5265901 ,MinGW离线压缩包

C语言的面向过程编程,C++的面向对象编程。

> C/C++语法及特性, .h头文件与.c源文件

  .h文件作为接口存在的。所谓接口就是指类型定义、变量声明、函数声明等等,基本上不会在.h里面放置函数实现。.c里面才真正实现函数。然后面向接口编程。struct结构变量(结构体)也声明在.h文件中、
 如果其可见性超出一个.c文件,那么应当放入.h中,如果只是某个.c里需要这么一个结构作为辅助,直接放入这个.c中更好一些。 .h的影响范围更大,如果有修改的话所有依赖的文件都要重新编译,会影响编译效率,主要就是基于这个考虑
 ——提供给别人的接口越隐藏细节,接口越简单,耦合越少,越容易修改。但是把结构体定义在.c文件使用的人就不能在栈上分配内存了。
 ——放c还是h取决于该结构是否要暴露给其他c,原则就是能放c绝不放h,但一般来说既然定义了结构,多半还是要暴露出来的.
-- 综合来说,
1.全局变量一般都用static包裹,然后提供配套的set和get接口给别人调用,放在.c文件中。
2.结构体、枚举、宏(名字尽量复杂点,避免和别人的宏重复)、定义放在.h中,可以让别人include,提高代码的复用率。

> C/C++ 结构化编程,面向对象编程
数据类型、运算符与表达式(常量与变量,整形 字符类型,+-*/)
顺序程序设计(1.顺序结构:2.选择(判断)结构:3.循环结构,赋值 putchar getchar printf scanf)
顺序结构,选择(判断)结构(if switch),循环结构(goto while for break continue)
一维、二维数组,字符数组,字符串和字符串结束标志
函数,内部函数 外部函数(全局变量局部变量,auto static register extern )
预处理命令(带宏和不带宏,文件包含)

指针:一维指针 二维指针 多维指针

C语言位运算符:与、或、异或、取反、左移和右移- http://www.cnblogs.com/yezhenhan/archive/2011/11/06/2238452.html

> * 或&用法

&和*在C语言中的含义:& 取地址,* 取值
&x是对x变量取地址,也就是返回的是x的地址。 int *i;这里面的*说明变量i是一个指针,存的是一个地址。
而printf("%d\n",*i);这里面的*的作用是返回指针i所指的变量。
  & 是取地址符号,既取得某一个变量的地址,如:&a;而* 是指针运算符(乘号就不说了),可以表示一个变量是指针类型搜索;也可以表示一个指针变量的所指向的存储单元,可以获取某个地址存储的值。
  * 两种,一种乘法 一种用于表示指针的内容;&两种 一种位与运算,一种是取变量地址.
-- 三个函数只是形参不一样而已:
 f(int s)  //形参是s,int s表示:函数自己开设一个变量s存放传入的整型数值,以便函数内使用;
 f(int *s) //形参是指针,int *s表示:函数开设一个指针变量s,存放传入的某变量的地址,函数内用*s的方法可访问这个变量单元,函数结束这个变量被舍弃;
 f(int &s) //形参是引用,int &s表示:在调用函数时所给出的变量比如int a; f(a);这个变量在子程序中与变量a对应,改变s的值如同修改调用者变量a一样,视作同一单元的操作,也就是子程序通过变量名s直接访问调用者变量a。

#include<stdio.h>
int main()
{
    char  const * keyword[]={
        "1111",
        "2222",
        "3333",
        "4444",
        NULL};
    char **p=(char**)keyword;
    while(*p!=NULL)
    {
        puts(*p++);
    }
    return 0;
}

> 程序1
#include <stdio.h>  
int main(int argc, const char * argv[]) { 
  int b = 20;
  int *a = 0; 
  printf("%d,\n",&b); //取变量b的地址
  printf("%d,\n",a);  //取变量a的值
  return 0;  


> 程序2  *、&用法介绍
#include <stdio.h>
int find(int *a);
main(){
    int b = 1;
    int wait, test;
    test = find(&b);//int* a = &b,创建了一个新的指向b的整型指针a作为find函数范围内的局部变量。
    printf("内存地址为:%d\n",&test);
    printf("内存地址为:%d\n",test);
    scanf("%d", &wait);
    
}
int find(int *a){
    return *a;
}

> 程序3
#include <stdio.h>
#include <string.h>
int main(){
  int a = 97;
  char ch = *(char *)&a; //*(char *) &a: 含义就是先取a的首地址,然后强制转换为char指针类型,然后取该指针的值
  printf("ch is %c\n",ch);
}

> 程序4  &作为取地址操作时,其行为结果是在编译时就被确定的;而*,解引用操作(或取内容)操作,其行为结果只能在运行时才可被确定
//&作为取地址操作,当且仅当&后面跟着的是变量或函数标识符。所以这里的&表示脱去解引用
#include "stdio.h"
int main(void)  {  
    int a = 0; // &a = 0x0012ff60  
    printf("%d,\n",&a);
    //*(int*)&a表示取变量a的内容
    int *p = &*(int*)&a;//这里的&不是取地址,因为一个*(int*)0x0012ff60不是变量,它是没有地址的 
    printf("The value is: %d\n", *p);  
    return 0;  
}

> 程序4,上面程序4的变样
#include "stdio.h"
int main(void) 

    int a = 0;
    int *p = &*&*&a;
    printf("The value is: %d\n", *p);
    return 0; 
}

> 程序5 ,在C++中,&还可以表示引用,
#include "iostream" 
using namespace std;
int main(void){ 
    int a = 0;
    int &r = a;
    cout << "The value is: " << r << endl;
    return 0; 
}

> C的数据类型及基本数据类型printf()对应格式

  C语言的执行过程:源程序---编译--链接--执行
编译:将源程序编译生成 .o的目标文件(快捷方式 command +b);
链接:链接库文件,将目标文件生成 .out的可执行文件。(快捷方式 command +r)。

数据类型   关键字:void int  char float double (5)
类型修饰符关键字:short long signed unsigned(4)
复杂类型    关键字:struct  enum    union(3) 
流程控制      关键字: for break continue do while   if else  goto  suitch case default  return(12)
存储类型     关键字:auto extern  static  regist ( 4)
其他            关键字:const sizeof  typedel    volatile(4)

常用基本数据类型占用空间(64位机器为例)int* p2=(int*)malloc(sizeof(int));
char:1个字节 ; int:4个字节; float:4个字节; double:8个字节

-- C语言的基本语法- http://blog.csdn.net/ww231147/article/details/49837561
1 整型,int, 使用格式为%d;
2 短整型,short, 使用格式为%h;
3 长整型,long, 使用格式为%ld;
4 字符型,char, 使用格式为%c;
5 字符指针型,char *, 使用格式为%s;
6 单精度浮点型,float, 使用格式为%f;
7 双精度浮点型,double, 使用格式为%lf。
%d 十进制有符号整数 
%u 十进制无符号整数 
%f 浮点数 
%s 字符串 
%c 单个字符 
%p 指针的值 
%e 指数形式的浮点数 
%x, %X 无符号以十六进制表示的整数 
%0 无符号以八进制表示的整数 
%g 自动选择合适的表示法 
%3d 表示输出3位整型数, 不够3位右对齐。 
%9.2f 表示输出场宽为9的浮点数, 其中小数位为2, 整数位为6,小数点占一位, 不够9位右对齐。 
%8s 表示输出8个字符的字符串, 不够8个字符右对齐。 

-- c语言中基本数据类型printf()对应格式- http://www.cnblogs.com/xiaobaizhu/articles/2783868.html
#include <stdio.h> 
#include <string.h> 
int main() { 
  char c, s[20], *p,ccc; 
  int b= 12;
  int a=1234, *i; 
  float f= 3.141592653589; 
  double x= 0.12345678987654321; 
  p="How do you do"; 
  strcpy(s, "Hello, Comrade"); 
  i= &b;//TODO
  c='\x41'; 
  printf("a=%d\n", a); /*结果输出十进制整数a=1234*/ 
  printf("a=%6d\n", a); /*结果输出6位十进制数a= 1234*/ 
  printf("a=%06d\n", a); /*结果输出6位十进制数a=001234*/ 
  printf("a=%2d\n", a); /*a超过2位, 按实际值输出a=1234*/ 
  printf("*i=%4d\n", *i); /*输出4位十进制整数*i= 12*/ 
  printf("*i=%-4d\n", *i); /*输出左对齐4位十进制整数*i=12*/ 
  printf("i=%p\n", i); /*输出地址i=06E4*/ 
  printf("f=%f\n", f); /*输出浮点数f=3.141593*/ 
  printf("f=6.4f\n", f); /*输出6位其中小数点后4位的浮点数 f=3.1416*/ 
  printf("x=%lf\n", x); /*输出长浮点数x=0.123457*/ 
  printf("x=%18.16lf\n", x);/*输出18位其中小数点后16位的长浮点 数x=0.1234567898765432*/ 
  printf("c=%c\n", c); /*输出字符c=A*/ 
  printf("c=%x\n", c); /*输出字符的ASCII码值c=41*/ 
  printf("s[]=%s\n", s); /*输出数组字符串s[]=Hello, Comrade*/ 
  printf("s[]=%6.9s\n", s);/*输出最多9个字符的字符串s[]=Hello, Co*/ 
  printf("s=%p\n", s); /*输出数组字符串首字符地址s=FFBE*/ 
  printf("*p=%s\n", p); /* 输出指针字符串p=How do you do*/ 
  printf("p=%p\n", p); /*输出指针的值p=0194*/ 
  return 0; 

> const和define关键字声明常量及指针

C语言中我们可以使用const和define关键字声明常量,所谓常量就是指值不能修改的量。如下面的例子所示:
1> int const a; const int a; //这两条语句都把a声明为一个常量(整数),它的值不能被修改。
2> 在声明时对其进行初始化 int const a = 15;  #define PI 3.14  // 定义了一个常量为3.14的宏PI,
3> int *pi; pi是一个普通的指向整形的指针。
4> 而变量int const *pci;则是一个指向整型常量的指针。你可以修改指针的值,但你不能修改它所指向的值
5> int *const cpi;则声明cpi为一个指向整型的常量指针。此时指针是常量,它的值(内存值?)无法修改,但你可以修改它所指向的整型的值。
6> int const *const cpci; 无论是指针本身还是它所指向的值都是常量,不允许修改。

>  .与->的区别?
 ->在C语言中称为间接引用运算符,是二目运算符,优先级同成员运算符“.”。
 用“.”的话,只需要声明一个结构体。格式是,结构体类型名+结构体名。然后用结构体名加“.”加域名就可以引用域 了。因为自动分配了结构体的内存。如同 int a;一样。用->的话,要声明一个结构体的指针,还要手动开辟一个该结构体的内存,然后把返回的指针给声明的结构体指针。才能用->正确引用。否则内存中只分配了指针的内存,没有分配结构体的内存,想要的结构体实际上是不存在。这时候用->引用自然出错了,因为没有结构体,自然没有结构体的域了。
 "."我直接读做"的”。->我读作"指向的结构体的"。
 存的地方不同吧,用.的是在栈里的局部变量,局部变量在方法执行完后会清空栈,所以不用free垃圾回收,这时整修结构体都在栈中。而->是在栈中声明了一个指针,指向堆中的结构体,这样方法运行完后只会清空栈中的指针,所以需要手动free清空堆中的结构体。

p->a,其中p是指向一个结构体的指针,a是这个结构体类型的一个成员。表达式p->a引用了指针p指向的结构体的成员a。例如:
struct T{
   int a;
   char b;
}s;
struct T* p=&s;
那么,p->a相当于s.a。显然,有个等价写法:(*p).a,和p->a完全等效。
链表指针是C语言的一个难点,但也是重点,学懂了非常有用。要仔细讲就必须先讲变量、指针。

> C++:与::的区别?http://blog.sina.com.cn/s/blog_6754000a0101a8cf.html
1.冒号(:)用法
(1)表示机构内位域的定义(即该变量占几个bit空间)
typedef struct _XXX{
  unsigned char a:4;
  unsigned char c;
} ; XXX
(2)构造函数后面的冒号起分割作用,是类给成员变量赋值的方法,初始化列表,更适用于成员变量的常量const型。
struct _XXX{
  _XXX() : y(0xc0) {}
};
(3) public:和private:后面的冒号,表示后面定义的所有成员都是公有或私有的,直到下一个"public:”或"private:”出现为止。"private:"为默认处理。
(4)类名冒号后面的是用来定义类的继承。
class 派生类名 : 继承方式 基类名{
  派生类的成员
};
继承方式:public、private和protected,默认处理是public。

2.双冒号(::)用法
(1)表示“域操作符”
例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给出f的定义,那么在类外定义f时, 
就要写成void A::f(),表示这个f()函数是类A的成员函数。
(2)直接用在全局函数前,表示是全局函数 
例:在VC里,你可以在调用API 函数里,在API函数名前加::
(3)表示引用成员函数及变量,作用域成员运算符
例:System::Math::Sqrt() 相当于System.Math.Sqrt()

VC中如下:
 ::是C++里的“作用域分解运算符”。比如声明了一个类A,类A里声明了一个成员函数voidf(),但没有在类的声明里给出f的定义,那么在类外定义f时,就要写成voidA::f(),表示这个f()函数是类A的成员函数。
 :: 一般还有一种用法,就是直接用在全局函数前,表示是全局函数。当类的成员函数跟类外的一个全局函数同名时,考试,大提示在类内定义的时候,打此函数名默认调用的是本身的成员函数;如果要调用同名的全局函数时,就必须打上::以示区别。比如在VC里,你可以在调用API函数时,在API函数名前加::。

> C的IDE 编译器
爱手工用手工,爱makefile用makefile,爱ide用ide。
在Windows平台下,用MinGW + EditPlus。vim gcc gdb.
编辑好的源码是怎么变成可执行文件的,什么预处理器、编译器、链接器、运行时库一概不知
我的IDE使用路径提供参考: Turbo C,  Borland C++,  VC++,  Eclipse, Emacs + Toolchains
linux下没什么ide可言,就是主要需要一个文本编辑器。gedit,vim都可以用。用惯了windows的人肯定更习惯用gedit编辑代码。Visual Studio自带的
Linux Platform:
Editor: Vim7.4;
tag: ctags + cscope;
Compiler: gcc4.9, clang-3.5;
Debugger: gdb, lldb-3.5
IDE: vim, emacs, slickedit, vc,source insight, codeblock, ultraedit, eclipse, 太多了。编译器,主要就是vc, gcc, llvm
在windows平台,初学者还是用C-free吧,简单小巧容易上手,当然做项目肯定是不行的,水平提高了那也只能用visual studio。Linux平台推荐vim+gcc+gdb,eclipse C++也不错。

> 指针的指针,C;C语言动态和静态内存分配与回收???
-- 内存分配方式:  
  (1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。  
  (2) 在栈上创建。在执行函数时,函数内部局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放,栈内存分配运算内置于处理  器的指令集中,效率很高,但是分配的内存容量有限。  
  (3) 从堆上分配。亦称动态内存分配。程序在运行的时候用malloc或new申请任意多的内存,程序员自己负责在什么时候free或delete释放内存。动态内存  的生存期有程序员决定,使用非常灵活,但问题也最多。

//c语言静态内存分配,静态内存是程序编译执行后系统自动分配,由系统自动释放,静态内存是栈分配的。
#include <stdio.h>  
#include <stdlib.h>    
void func(int** address )  {   
    //定义int类型的i变量,并赋值100  
    int i = 100;  
    //把i对应的地址赋值给iPoint变量  
    *address = &i;  
}  
int main(int argc, char *argv[])  {   
    //定义int类型的一级指针变量iPoint  
    int* iPoint;  
    func(&iPoint);  
    printf("*iPoint==%d\n",*iPoint); //打印值为100  
    printf("*iPoint==%d\n",*iPoint); //打印值为-2 (变为垃圾值)  
  
    system("pause");  
    return 0;  


//c语言动态内存分配,动态内存是开发者手动去分配的,是堆分配的。
#include <stdio.h>  
#include <stdlib.h>    
void func(int** address )  {     
    int i = 100;  
    int* temp;  
    //malloc(int)  --内存地址  此方法返回的是内存地址  
    //temp=(int *)malloc (sizeof (int))与temp=malloc (sizeof (int))是有区别的 
    temp = (int*)malloc(sizeof(int)); //malloc(sizeof(int)) 
    //把i对应的值,赋值给temp地址对应的值  
    *temp = i;  
    //把address对应的地址对应的值修改成temp  
    *address = temp;  
    //free(temp); //释放内存  
}   
int main(int argc, char *argv[]) {   
    //定义int类型的一级指针变量iPoint  
    int* iPoint;  
    func(&iPoint);  
    printf("*iPoint==%d\n",*iPoint); //打印值为100  
    printf("*iPoint==%d\n",*iPoint); //打印值为100 (内存没有被回收)  
  
    system("pause");  
    return 0;  
}

  c语言提供内存动态分配的函数有:malloc、calloc、realloc,在使用这些函数时必须包含其头文件,分别为:<malloc.h>、<stdlib.h>、<alloc.h>。
  在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可以用于申请动态内存和释放内存。
对于非内部数据类型对象而言,光用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free.
   堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由函数malloc进行分配。不过栈的动态分配和堆不同,他的动态分配是由编译器进行释放,无需我们手工实现。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。

> 一级指针和二级指针

  void fun(int *p),指针参数p的副本为_p,编译器使_p=p,_p和p指向相同的内存空间,如果在函数内修改了_p所指向的内容,就会导致p的内容也做相应的改变;但如果在函数内_p申请了新的内存空间或者指向其他内存空间,则_p指向了新的内存空间,而p依旧指向原来的内存空间,因此函数返回后p还是原来的p。这样的话,不但没有实现功能,反而每次都申请新的内存空间,而又得不到释放,因为没有将该内存空间的地址传递出来,容易造成内存泄露。
  void fun(int **p),如果函数参数是指针的地址,则可以通过该参数p将新分配或新指向的内存地址传递出来,这样就实现了有效的指针操作。
 -- 指针也是值传递
一级指针所关联的是其值(一个地址)名下空间里的数据,一级指针:即我们一般说的指针,就是内存地址;
二级指针又分为指向指针变量的指针和指向数组的指针,二级指针:指向指针的指针,就是地址的地址;

 -- 数组指针与指针数组(一些notes):
数组指针:int(*p)[4] 指向数组p的指针 a pointer to an array
指针数组:int *p[4] or int *(p[4]) 数组p中元素都为int型指针array of pointers,[ ]的优先级高于*
#include <stdio.h>  
#include <stdlib.h>   
int main(){
 int a= 1;
 int *p=&a;
 int **q=&p;
 printf("*iPoint==%d\n",*p); //打印值为1
printf("*iPoint==%d\n",&a); //打印值为地址值   
 printf("*iPoint==%d\n",**q); //打印值为1 
 return 0;
}

 --在传递一级指针时,只有对指针所指向的内存变量做操作才是有效的;
在传递二级指针时,只有对指针的指向做改变才是有效的;
#include<stdio.h>
 void  fun(int  *p){
    int  b=100;
    p=&b;
}
int main(){
    int  a=10;
    int  *q;
    q=&a;
    printf("%d\n",*q);
    fun(q);
    printf("%d\n",*q);
    return  0;
}//运行结果:10  10

#include<stdio.h>
void  fun(int  **p){
    int  b=100;
    *p=&b;
}
int main(){
    int  a=10;
    int  *q;
    q=&a;
    printf("%d\n",*q);
    fun(&q);
    printf("%d\n",*q);
    return  0;
}//运行结果:10  100

二级指针在链表中的使用:在链表或者树的操作中,也需要用到二级指针,
比如创建链表的头指针:
 在初始化链表函数中,传入头指针,并在函数中为该指针分配空间,此时就应该使用二级指针,如void initLinklist(Node **head);
 而在添加删除结点的过程中,我们并没有改变函数参数指针的指向,而是通过传入的指针如Node *head,找到要删除结点的位置,并未对该指针做改变,因此退出函数后,该指针无影响。
  
 一级指针存放变量的地址,指向的值是变量的内容。如int* p={1,2,3}, p=数组的首地址,*p=数组的第一个值;
 二级指针存放一级指针的地址,指向一级指针。如int*p ={1,2,3}, int**pp=&p,pp=指针p的首地址,*pp=数组的首地址,**pp=数组第一个值1。
 在作为参数传递时什么时候用一级指针,什么时候用二级指针?
 一级指针作为参数传递时,由实参赋予形参对其指向内容的修改的能力,但是一旦修改形参的指向使其指向其它地方,则这种改变不会返回给实参,此时若要使实参的指针指向地址(指针本身的值)发生改变则使用二级指针。

猜你喜欢

转载自blog.csdn.net/ShareUs/article/details/72678165