//避免在.c里面重复包含多次头文件
#ifndef _CLT_SOCKET2_H__
#define _CLT_SOCKET2_H__
- //...
#endif
//因为数据类型不一样
数据类型的本质:
数据类型可理解为创建变量的模具(模子),是固定内存大小的别名。
数据类型的作用:
编译器预算对象(变量)分配的内存空间大小。
int a[10] = { 1,5,7,9,3,4,6,8,2,0 };
a 与 &a 数据类型不一样,步长不一样
typedef int u32;可以为数据类型起别名。
数据类型封装1
1、void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。
2、用法1:数据类型的封装
int InitHardEnv(void **handle);
典型的如内存操作函数memcpy和memset的函数原型分别为
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );
3、用法2: void修饰函数返回值和参数,仅表示无。
如果函数没有返回值,那么应该将其声明为void型
如果函数没有参数,应该声明其参数为void
int function(void)
{return 1;}
4、void指针的意义 就是把一个不知道数据类型的地址传递过去
C语言规定只有相同类型的指针才可以相互赋值
void*指针作为左值用于“接收”任意类型的指针
void*指针作为右值赋值给其它指针时需要强制类型转换
int *p1 = NULL;
Char *p2 = (char *)malloc(sizoeof(char)*20);
5、不存在void类型的变量
C语言没有定义void究竟是多大内存的别名
6、扩展阅读《void类型详解.doc》
变量 本质是什么?(一段连续)内存空间的别名,变量是一个标号
概念:既能读又能写的内存对象,称为变量;若一旦初始化后不能修改的对象则称为常量。
变量定义形式: 类型 标识符, 标识符, … , 标识符 ;
变量本质:(一段连续)内存空间的别名
1、程序通过变量来申请和命名内存空间 int a = 0
2、通过变量名访问内存空间
修改变量有几种方法?
1、直接
2、间接。内存有地址编号,拿到地址编号也可以修改内存;于是指针横空出世了!
3、内存空间可以再取给别名吗?可以取别名,比如C++的引用,引用的本质就是给变量起了个别名,引用和变量的本质不同。
程序的内存四区模型
void main(){
int a = 10; //分配4个字节的内存在栈区,栈区也叫临时区
int *p ;//分配4个字节的内存
p = &a; //CPU执行的代码,放在代码区
*p = 20;//
{
char *p =NULL;//分配4个字节的内存
p = (char *)malloc(100);
if(p != NULL){
free(p);
}
}
system("pause");
}
流程说明
1、操作系统把物理硬盘代码load到内存
2、操作系统把c代码分成四个区
3、操作系统找到main函数入口执行
内存四区模型-各个元素分析
指针变量和它所指向的内存空间变量是两个不同的概念
指针指向谁,就把谁的地址赋给指针。
内存四区概念
函数1调用函数2,函数1称为主调函数 函数2称为被调用函数 |
规则1:Main(主调函数)分配的内存(在堆区,栈区、全局区)都可以在被调用函数里使用吧。 |
规则2:在被调用函数里面分配的内存 1、如果在被调用函数里面的临时区(栈)分配内存,主调用函数是不能使用的。 |
char * getstring3() { char buf[30]; strcpy(buf, "abcde"); return buf; } |
如何建立正确的程序运行内存布局图
内存四区模型&函数调用模型
函数内元素
深入理解数据类型和变量“内存”属性
一级指针内存布局图(int *,char*)
二级指针内存布局图(int ** char **)
函数间接
主调函数分配内存,还是被调用函数分配内存
主调函数如何使用被调用函数分配的内存(技术关键点:指针做函数参数)
栈的模型生态是开口向下的。堆的模型生态是开口向上。
Heap、Stack生长方向和内存存放方向是两个不同概念。
*p 解释: *就像一把钥匙,根据一个指针变量的值,去修改门后面的内存空间。
指针变量和它指向的内存块是两个不同的概念
//含义1 给p赋值p=0x1111; 只会改变指针变量值,不会改变所指的内容;p = p +1; //p++
//含义2 给*p赋值*p='a'; 不会改变指针变量的值,只会改变所指的内存块的值
//含义3 =左边*p 表示 给内存赋值, =右边*p 表示取值 含义不同切结!
//含义4 =左边char *p
//含义5 保证所指的内存块能修改
指针也是一种数据类型,占4个字节
#include "stdio.h"
#include "stdlib.h"
//char *p1 形参 形参也是被调用该函数的参数,只不过具有对外属性而已。
//
void getData01(char *p1)
{
printf("getData01() begin ");
return ;
}
////char *p1 形参 是变量
void getData02(char **p2)
{
printf("getData01() begin ");
return ;
}
////char *p1 形参 是变量
void getData03(char **p3)
{
printf("getData01() begin ");
return ;
}
////char *p7 形参 是变量
void getData07(char *******p7)
{
printf("getData01() begin ");
return ;
}
/*
//对参数的指针类型应该怎么理解
//理解角度需要从两个角度出发
//站在c/c++编译器的角度 对形参,如果是指针类型,c编译器只会分配四个字节的内存。
////char *p7 形参 是变量
//指针的数据类型到底是什么
指针的数据类型是指它所指向的内存空间的数据类型
指针的数据类型具有依附特性
结论:指针的步长,根据所指内存空间类型来定。
void senddata01(char *p1); void senddata01(char* p1);
void senddata02(char ** p1); void senddata02(char * *p1); void senddata02(char **p1);
void senddata03(char ***p1);
void senddata04(char *p[]); void senddata04(char * p[]); void senddata04(char *p []);
void senddata05(char (*p)[10]); void senddata05(char (*p) [10]);
void senddata05(char *****p4);
*/
void main61()
{
char *p1 = NULL;
char **p2 = NULL;
int a = 10;
int *p3 = NULL;
int c = 0;
a = 20; //直接通过变量修改a的值
p3 = &a; //
//*p放在=号的左边,去修改内存空间的值。
*p3 = 30; //通过指针间接修改内存空间的值
c = *p3; //*p放在等号的右边从内存空间中拿值
/*
while(*p != ' '')
*p1++ = *p2++
*/
//*p 的意义:*就像一把钥匙,根据一个指针变量的值,去修改门后门的内存空间
{
char *p2 = 0x0077;
*p2 = 100;
*((int *)77) = 100;
}
printf("p2:%d ", sizeof(p2));
system("pause");
}
void main63()
{
char *p1 = NULL;
char *p2 = NULL;
char buf1[100] = {0};
char buf2[100] = {0};
strcpy(buf1, "abcdefg");
p1 = buf1;
p2 = buf2;
while(*p1 != ' ')
{
*p2 = *p1;
p2++; //p2 向后跳动1个字节
p1++;
}
}
void main()
{
// {
// char *p = NULL;
// *p = 100;
//
// }
// {
// char *p = 0x77;
// *p = 100;
//
// }
{
char *p = "abcdefg";
p = 100;
printf("%x ", p);
*p = 'z';
}
system("pause");
}
//10 字面量,放在不能取地址 没有放在堆栈、全局区,可以按照放在代码区域之类的区域内理解它。
int *a = &10;
指针是一种数据类型,是指它指向的内存空间的数据类型。
含义1:指针步长(p++),根据所指内存空间的数据类型来确定。
p++ = -->> (unsigned char)p+sizeof(a);
结论:指针的步长,根据所指内存空间类型来定、
通过*p/*p++来改变变量的值是指针存在的最大意义
1)两码事:指针变量和它指向的内存块的变量
2)条件反射:指针指向某个变量,就是把某个变量地址赋给指针
2)条件反射:指针指向某个变量,就是把某个变量地址赋给指针
3)*p间接赋值成立条件:3个条件
1,2个变量(通常一个实参,一个形参)
2,建立关系,实参取地址赋给形参指针
3,*p形参,求间接的修改实参 的值
1.2.3是指,上述,1,2,3个条件
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
//指针做函数参数
- //函数调用过程中,
- //用1级指针(通常是形参,)去修改0级指针(通常是实参)的值
- //用2级指针(通常是形参,)去修改1级指针(通常是实参)的值
- //用3级指针(通常是形参,)去修改2级指针(通常是实参)的值
- //用8级指针(通常是形参,)去修改7级指针(通常是实参)的值
- //用n级指针(通常是形参,)去修改n-1级指针(通常是实参)的值
int getLen(int *p) {
*p = 40; //形参通过地址间接的修改实参的值
return 0;
}
/*
间接赋值的应用场景有2个
1.在函数之内 *p1++ = *p2++
2.指针做函数参数,通过*p形参去间接的修改实参的值,这才是指针存在的最大意义
这才是C语言特有的现象,才是指针的精华。
*/
void main() {
int a = 10; //定义了一个变量(实参)
int *p = NULL; //定义了一个变量 (形参)
a = 20; //直接修改a的值
printf("a: %d ", a);
p = &a;//把a的地址赋给p //把一个变量的地址传递给另外一个变量 //实参取地址传给形参
*p = 30;//间接赋值,如果p是a的地址,那么就间接的修改a的值 //*p放在等号的左边
printf("a: %d " ,a );
//p = &a; //a的地址赋给p,把这句话,转成函数调用
getLen(&a);
printf("a: %d ", a);
system("pause");
}
//[] *
//[] -----> *
//buf[i] --> bur[0+i] ---> *(p+1) -->p[i]
//站在C++编译器的角度,*p相当于我们程序猿手工(显示)利用间接赋值,去操作内存
//[]怎么理解?只不过是C++编译器帮我们程序猿做了*p操作
*to++ = *from++;先执行*to 再执行++; ++的优先级高于*;