0. 指针到底是什么?
在我们讨论指针的定义之前,让我们先了解一下当我们编写时会发生什么
c
int digit = 42;
编译器保留一块内存来保存int
值。该块的名称为digit
,该块中存储的值为42
。现在,为了记住该块,它被分配了一个地址或一个位置号(例如 24650)。
位置编号的值对我们来说并不重要,因为它是一个随机值。但是,我们可以使用&
(与符号)或运算符地址来访问该地址,如下所示。
c
printf("The address of digit = %d.",&digit);
/* prints "The address of digit = 24650. */
现在,我们可以使用另一个运算符(星号)digit
从变量的地址获取变量的值,称为间接 或取消引用 或地址运算符的值 。*
c
printf("The value of digit = %d.", *(&digit);
/* prints "The value of digit = 42. */
1. 指针定义和表示法
的地址digit
可以存储在另一个称为指针变量的变量中。将变量地址存储到指针的语法是:
c
dataType *pointerVariableName = &variableName;
对于我们的digit
变量,可以写成:
c
int *addressOfDigit = &digit;
或者
c
int *addressOfDigit;
addressOfDigit= &digit;
这可以理解为 -指向int
(整数)addressOfDigit
存储address of(&)
digit
变量的指针。
需要理解的几点------
dataType
- 我们需要告诉计算机我们要存储其地址的变量的数据类型是什么。这里,int
是 的数据类型digit
。
这并不 意味着addressOfDigit
将存储类型的值int
。
整数指针(如addressOfDigit
)只能存储整数类型变量的地址。
c
int variable1;
int variable2;
char variable3;
int *addressOfVariables;
variable1
在这里,我们可以将和的地址分配variable2
给整数指针 addressOfVariables
,但不能分配给 ,variable3
因为它的类型是char
。我们需要一个字符指针变量来存储其地址。
*
- 指针变量是一种特殊 变量,它用于存储另一个变量的地址。为了与其他不存储地址的变量区分开来,我们*
在声明中使用 , 作为符号。
现在,我们可以使用addressOfDigit
指针变量来打印地址和值,digit
如下所示:
c
printf("The address of digit = %d.", addressOfDigit);
/* prints "The address of digit = 24650." */
printf("The value of digit = %d.", *addressOfDigit);
/*prints "The value of digit = 42. */
这里,*addressOfDigit
是被读取为存储在地址处的值addressOfDigit
。
请注意,我们使用的格式 %d
标识符。嗯,这并不完全正确。要使用的正确标识符是。addressOfDigit
%p
使用%p
,地址以十六进制值显示。但内存地址可以以整数和八进制值显示。尽管如此,由于这不是完全正确的方法,因此会显示警告。
c
int num = 5;
int *p = #
printf("Address using %%p = %p",p);
printf("Address using %%d = %d",p);
printf("Address using %%o = %o",p);
根据我使用的编译器的输出是 -
c
Address using %p = 000000000061FE00
Address using %d = 6422016
Address using %o = 30377000
这是您使用时显示的警告%d
-
c
warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *'
2. 一些特殊提示
1. 野指针
c
char *alphabetAddress; /* uninitialised or wild pointer */
char alphabet = "a";
alphabetAddress = &alphabet; /* now, not a wild pointer */
当我们定义字符指针时alphabetAddress
,我们没有初始化它。此类指针称为野指针 。它们存储一个垃圾值,即我们不知道是否保留的字节的内存地址(请记住int digit = 42;
,我们在声明它时保留了一个内存地址)。
假设我们取消引用一个野指针并将一个值分配给它指向的内存地址。这将导致意外的行为,因为我们将在可能空闲或保留的内存块中写入数据。
2. 空指针
现在,为了确保我们没有野指针,我们可以用一个NULL
值初始化一个指针,使其成为空指针。
c
char *alphabetAddress = NULL /* Null pointer */
空指针不指向任何内容,或者指向用户无法访问的内存地址。
3. 空指针
void 指针可用于指向任何数据类型的变量。它可以被重用来指向我们想要的任何数据类型。它被声明为
c
void *pointerVariableName = NULL;
由于它们本质上非常通用 ,因此也称为通用指针。
由于其灵活性,void 指针也带来了一些限制。空指针不能 像任何其他指针一样取消引用。适当的类型转换是必要的。
c
void *pointer = NULL;
int number = 54;
char alphabet = "z";
pointer = &number;
printf("The value of number = ", *pointer); /* Compilation Error */
/* Correct Method */
printf("The value of number = ", *(int *)pointer); /* prints "The value at number = 54" */
pointer = &alphabet;
printf("The value of alphabet = ", *pointer); /* Compilation Error */
printf("The value of alphabet = ", *(char *)pointer); /* prints "The value at alphabet = z */
类似地,void 指针需要进行类型转换才能执行算术运算。
空指针在 C 语言中很有用malloc()
。库函数calloc()
可以动态分配内存,返回空指针。qsort()
是 C 中的内置排序函数,有一个函数作为其参数,该函数本身接受 void 指针作为其参数。
4. 悬空指针
悬空指针指向用于保存变量的内存地址。由于它指向的地址不再被保留,使用它会导致意想不到的结果。
c
main(){
int *ptr;
ptr = (int *)malloc(sizeof(int));
*ptr = 1;
printf("%d",*ptr); /* prints 1 */
free(ptr); /* deallocation */
*ptr = 5;
printf("%d",*ptr); /* may or may not print 5 */
}
尽管内存已被释放free(ptr)
,但指向整数的指针ptr
仍然指向该未保留的内存地址。
3. 指针运算
现在我们知道指针与任何其他变量不同。除了内存块的地址之外,它们不存储任何值。因此,很明显,并非所有算术运算都对它们有效。两个指针(有地址)相乘或相除有意义吗?
指针的有效操作 很少但非常有用-
1.只有当一个指针具有相同类型时,才可以将它们的值分配给另一个指针(除非类型转换或其中一个是void *
.
c
int ManU = 1;
int *addressOfManU = &ManU;
int *anotherAddressOfManU = NULL;
anotherAddressOfManU = addressOfManU; /* Valid */
double *wrongAddressOfManU = addressOfManU; /* Invalid */
2.只能对指针进行整数加减。
c
int myArray = {3,6,9,12,15};
int *pointerToMyArray = &myArray[0];
pointerToMyArray += 3; /* Valid */
pointerToMyArray *= 3; /* Invalid */
当您向指针添加(或减去)一个整数(例如 n)时,您实际上并不是在字面上添加(或减去)该整数。 您正在添加(或减去)指针所指向的变量的数据类型大小的 n 倍。
c
int number = 5;
/* Suppose the address of number is 100 */
int *ptr = &number;
int newAddress = ptr + 3;
/* Same as ptr + 3 * sizeof(int) */
存储的值newAddress
不会是 103,而是112
.
3.指针的减法和比较仅当两者都是同一数组的成员时才有效。
c
int myArray = {3,6,9,12,15};
int sixthMultiple = 18;
int *pointer1 = &myArray[0];
int *pointer2 = &myArray[1];
int *pointer6 = &sixthMuliple;
/* Valid Expressions */
if(pointer1 == pointer2)
pointer2 - pointer1;
/* Invalid Expressions
if(pointer1 == pointer6)
pointer2 - pointer6
指针相减得到分隔它们的元素数量。
4.您可以对指针进行赋值或与 进行比较NULL
。
上述规则的唯一例外是数组最后一个元素之后的第一个内存块的地址遵循指针算术。
指针和数组同时存在。最有效的指针操作只能通过数组来完成是有原因的。我们将在下一篇文章中用数组讨论上述规则。