指针和数组等价吗?
数组名并不完全是一个指向数组首地址的指针。
数组名在编译器中常被定义为一个常量指针,数组名不被看成是指针常量是情况有以下两种:
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;
}
运行结果