C基础梳理 2019.1.2(数组首元素指针类型和数组首元素指针类型、结构体嵌套一级指针)

指针和数组等价吗?

数组名并不完全是一个指向数组首地址的指针

 

数组名在编译器中常被定义为一个常量指针,数组名不被看成是指针常量是情况有以下两种:

1、数组名作为sizeof操作符的操作数时,此时sizeof返回的是整个数组的长度,而不是数组指针的长度。

2、数组名作为&操作符的操作数时,返回的是一个指向数组的指针(该指针的步长为整个数组的大小,他是一个数组指针类型),而不是指向某个数组元素的指针常量。

#include<stdio.h>
#include<stdlib.h>

void test() {
	int arr[] = { 1,2,3,4 ,5,6,7,8,9};
	printf("&arr%d   &arr+1%d\n", &arr, &arr + 1);
}
int main() {
	test();
	return 0;
}

运行结果:

 

 

数组首元素指针类型和数组首元素指针类型

数组指针类型——该指针的步长为整个数组的大小

数组首元素指针类型——该指针的步长为一个数组元素的大小

 

 

如何定义一个可以指向数组的指针

 

方法一:先定义数组类型,在定义数组指针类型

//加个括号是为了表示这是一个数组名
typedef int (ARRAY_TYPE)[5];
ARRAY_TYPE myarray;//等价于int myarray[5]

此时,这两个就是等价的了

//数组名取地址代表指向整个数组的指针
ARRAY_TYPE *pArray = &myarray;

 

对于指向数组的指针(因为他指向的是整个数组)进行解引用,该指针就退化成指向首元素的指针了。

#include<stdio.h>
#include<stdlib.h>

void test() {
	int arr[5] = { 1,2,3,4  };

	//加个括号是为了表示这是一个数组名
	typedef int(ARRAY_TYPE)[5];
	ARRAY_TYPE myarray;//等价于int myarray[5]

	ARRAY_TYPE *pArray = &myarray;
	pArray = &arr;

	printf("*(*pArray+1):%d\n", *(*pArray + 1));
}
int main() {
	test();
	return 0;
}

运行结果

 

 

方法二:直接定义数组指针类型

int arr[5] = { 1,2,3,4  };
typedef int(*ARRAY_POINTER)[5];
ARRAY_POINTER pArr = &arr;

 

 

方法三:直接定义数组指针变量

int arr[5] = { 1,2,3,4  };
int(*pArrParam)[5] = &arr;

 

 

 

二维数组名

对于二维数组同一位数组一样,除了sizeof对数组名取地址之外,那么数组名就是指向数组首元素的常指针

#include<iostream>
#include<stdio.h>
#include<stdlib.h>

void test() {
	int arr[5] = { 1,2,3,4  };
	printf("%s\n", typeid(arr).name());

}
int main() {
	test();
	return 0;
}

运行结果

 

 

下图中的方法可以获取数组中的 8  

 

 

 

 

指针数组的选择排序

遍历数组,选择最小的值放在最前面的位置,然后遍历最前面的位置之后的那段数组。(每一轮固定一个值的位置)

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void SelectSort(char **arr, int len) {
	int min = 0;
	char *temp = NULL;
	for (int i = 0; i < len; ++i) {
		min = i;
		for (int j = i + 1; j < len; ++j) {
			if (strcmp(arr[j], arr[min]) < 0) {
				min = j;
			}
		}
		if (min != i) {
			temp = arr[min];
			arr[min] = arr[i];
			arr[i] = temp;
		}
	}
}

void PrintArray(char **arr, int len){
	for(int i=0; i<len; ++i){
		printf("%s\n",arr[i]);
	}
}

void test() {
	char * arr[] = { "ddddd","aaaaa","ccccc","bbbbb" };
	int len = sizeof(arr) / sizeof(char*);
	SelectSort(arr, len);
	PrintArray(arr,len);
}

int main() {
	test();
	return 0;
}

运行结果

 

 

 

结构体

定义结构体时,不要直接给成员变量复制,结构体制是一个类型,编译器还没有为其分配空间,只有根据其类型定义变量时,才分配空间,有空间后才能赋值。

 

结构体变量的定义

下面的结构体由于没有名字,所以不能通过他再去定义新的对象了。

struct {
	int val;
}Obama;//定义类型同时定义变量

 

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Person{
	char name[64];
}Obama;

void test() {
	//对象指针用 ->   对象实体用 .
	strcpy(Obama.name, "lili");
	printf("name:%s\n",Obama.name);
}
int main() {
	test();
	return 0;
}

运行结果

 

 

结构体初始化

单个结构体

栈上初始化

struct Person{
	char name[64];
}Obama;

void test() {
	struct Person person = { "lili" };	
}

 

堆上初始化

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Person{
	char name[64];
}Obama;

void test() {
	struct Person *person = malloc(sizeof(struct Person));	
	strcpy(person->name, "lili");
	printf("Namw:%s", person->name);
}
int main() {
	test();
	return 0;
}

运行结果

 

多个结构体变量

栈上初始化

struct Person{
	char name[64];
};

void test() {
	//聚合初始化
	struct Person person[] = {
		{"aaa"},
		{"bbb"},
		{"ccc"}
	}
}

 

堆上初始化

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Person{
	char name[64];
};

void test() {
	struct Person *person = malloc(sizeof(struct Person) * 6);
	for (int i = 0; i < 6; ++i) {
		sprintf(person[i].name, "lili");
		printf("name:%s\n",person[i].name);
	}
}

int main() {
	test();
	return 0;
}

运行结果:

 

 

 

结构体深拷贝和浅拷贝

 

栈上

逐字节拷贝

下面代码中会申请两块内存空间person1和person2.当进入复制操作的时候,person1中的内容会被逐字节拷贝到person2中去。

struct Person{
	char name[64];
};

void test() {
	struct Person person1 = { "aaa" };
	struct Person person2 = { "bbb" };

	person2 = person1;

}

 

堆上

对于同一结构体的对象,结构体指针的size是一样的

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Person{
	char *name;	
};

void test() {
    struct Person person1;
    person1.name = (char*)malloc(sizeof(char) * 64);
    memset(person1.name, 0, 64);
    strcpy(person1.name, "aaa");

    struct Person person2;
    person2.name = (char*)malloc(sizeof(char) * 128);
    memset(person2.name, 0, 128);
    strcpy(person2.name, "bbbbbb");

    printf("person1 size :%d\n", sizeof(person1));
    printf("person2 size :%d\n", sizeof(person2));

    //释放内存
    if (person1.name != NULL) {
        free(person1.name);
        person1.name = NULL;
    }
    if (person2.name != NULL) {
        free(person2.name);
        person2.name = NULL;
    }

}

int main() {
	test();
	return 0;
}

运行结果:

 

 

注意,默认的结构体赋值是浅拷贝动作,会出现问题

如果上面的代码添加一句:person2=person1; 就会出现问题

因为同一块内存你会释放两次,解释如下图

 

 

因此我们可以得知:

如果结构体内有指针,并且指针指向堆空间。那么如果发生赋值行为,就会产生问题。

1、同一块内存被释放两次

2、内存泄漏。

 

 

将上述代码改成深拷贝

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Person{
	char *name;	
};

void test() {
	struct Person person1;
	person1.name = (char*)malloc(sizeof(char) * 64);
	memset(person1.name, 0, 64);
	strcpy(person1.name, "aaa");

        struct Person person2;
        person2.name = (char*)malloc(sizeof(char) * 128);
        memset(person2.name, 0, 128);
        strcpy(person2.name, "bbbbbb");

	printf("person1 size :%d\n", sizeof(person1));
	printf("person2 size :%d\n", sizeof(person2));

	//释放内存
	if (person1.name != NULL) {
		free(person1.name);
		person1.name = NULL;
	}

        //深拷贝
	person1.name=malloc(strlen(person2.name)+1);
	strcpy(person1.name, person2.name);
	
	if (person2.name != NULL) {
		free(person2.name);
		person2.name = NULL;
	}

}

int main() {
	test();
	return 0;
}

 

 

结构体指针

实现代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct Person{
	char* name;
	int age;
};

//分配内存
struct Person** allocateSpace(){
	struct Person **temp=malloc(sizeof(struct Person *)*3);
	
	//为每一个指针指向的地址分配内存
	for(int i=0; i<3; i++){
		temp[i]=malloc(sizeof(struct Person));
		//为每一个Person结构体中的name指针申请内存
		temp[i]->name=malloc(sizeof(char)*64);
		sprintf(temp[i]->name, "Name_%d", i+1);
		temp[i]->age=100+i;
	}
	return temp;
}

//打印
void print(struct Person **person){
	if(person==NULL) return;

	for(int i=0; i<3; ++i){
		printf("name:%s  age:%d\n", person[i]->name, person[i]->age);
	}
}

//释放内存
void freeSpace(struct Person **person){
	
	if(person==NULL) return;
	
	for(int i=0; i<3; i++){
		if(person[i]==NULL) continue;
		
		if(person[i]->name!=NULL){
			free(person[i]->name);
			person[i]->name=NULL;
		}		
		
		free(person[i]);
		person[i]=NULL;
	}

	free(person);
	person=NULL;
}

void test(){
	struct Person **person=NULL;
	person=allocateSpace();
	print(person);
	freeSpace(person);
}

int main(){
	test();
	return 0;
}

运行结果

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值