C#数据类型:值类型、引用类型、指针类型

数据类型

在 C# 中,变量分为以下几种类型:

  • 值类型(Value types)
  • 引用类型(Reference types)
  • 指针类型(Poninter types)

值类型

值类型变量可以直接分配给一个值。它们是从类System.ValueType中派生的。

值类型直接包含数据。比如int、char、float,它们分别存储数字、字符、浮点数。当您声明一个int类型时,系统分配内存来存储值,通常这个值会直接存储与栈中,而不是堆中。

值类型不可为空:int x = null (错误写法)。

可空值类型需要加?:int? x = null(正确写法)。

引用类型

引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的引用类型有:object、dynamic和string。

  • 对象(Object)类型

对象(object)类型是C#通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object是System.Object类的别名。所以对象(Object)类型可以被分配任何其它类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。

当一个值类型转换为对象类型时,则被称为装箱;另一方面,当一个对象类型转换为值类型时,则被称为拆箱

  •  动态(Dynamic)类型

可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在进行时发生的。

声明动态类型的语法:

dynamic <variable_name> = value;

例如:

dynamic a = 9;

动态类型与对象类型相识,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。

  • 字符串(String)类型

字符串(String)类型 允许您给变量分配任何字符串值。字符串(String)类型是System.String类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和@引号。

最常见的用法是直接双引号:

string str = "hello world";

C# string字符串的前面可以加@(称作“逐字字符串”)将转义字符串(\)当作普通字符对待,比如

string str = @"c:\directory";

上面代码的意义等价于

string str = "c:\\directory";

@字符串中可以任意换行,换行符及缩进空格计算在字符串长度之内。列如

1 string str = @"<script type=""text/javascript"">
2     <!--
3     -->
4 </script>";

指针类型

指针是变量的内存地址,涉及到指针的代码必须写到unsafe代码块(或方法中)。

指针变量声明的一般形式为:

type* var-name;

下面是指针类型声明的实例:

1 int* p;          //p 是指向整数的指针
2 double* p;       //p是指向双精度数的指针
3 float* p;        //p是指向浮点数的指针
4 int** p;         //p是指向整数的指针的指针
5 int*[] p;        //p是指向字符的指针
6 char* p;         //p是指向浮点数的指针
7 void* p;         //p是指向未知类型的指针

在同一个声明中声明多个指针时,星号 * 仅与基础类型一起写入;而不是用作每个指针名称的前缀。 例如:

1 int* p1, p2, p3;       // 正确  
2 int *p1, *p2, *p3;     // 错误 

赋值地址用&,列如

1 int i = 10;
2 int* iptr = &i;

其中 *iptr 是 iptr 的值,而&iptr 是 iptr 的地址。

因为类是托管类型,受GC影响,内存地址不是固定的,引用类型不可声明为指针。而指针分配内存后,内存是固定的,所以如果要使用类中的数据,就需要用fixed关键字临时固定类的指针。列如:

1 // 将peopleObj.Age的地址传给整型指针agePtr
2 fixed (int* agePtr = &peopleObj.Age)
3 {
4      *agePtr += 1; //这时peopleObj.Age的值就跟着变了      
5 }

fixed也可以用固定数组,例如:

 1 int[] list = {10, 20, 100};
 2 fixed(int* ptr = list)// 将整型数组赋值给ptr指针
 3  
 4 // 如何使用这个指针?
 5 // 如何获取地址?
 6 (int)(ptr + 0)    //内存地址33221102
 7 (int)(ptr + 1)    //内存地址33221106
 8 (int)(ptr + 2)    //内存地址33221110
 9 
10 //如何获取值?
11 *(ptr + 0)     //10
12 *(ptr + 1)     //20
13 *(ptr + 2)     //100

说明

C# 是一种高级编程语言,通常不鼓励直接使用指针,因为它们可以引起内存管理问题和安全问题。主要用于以下几种情况:

  1. 与非托管代码交互:在某些情况下,您可能需要与非托管代码(如C/C++库或操作系统API)进行交互。指针可以用于传递数据或引用非托管内存区域。这通常需要使用unsafe关键字。

  2. 性能优化:在某些情况下,使用指针可以提高性能,特别是在处理大型数据结构或执行计算密集型操作时。不过,应该小心使用指针,以避免潜在的错误和不安全的代码。

  3. 数据结构操作:某些数据结构(如树、图、矩阵等)可能需要直接操作内存以实现高效的算法。指针可以用于这些情况,但必须小心谨慎。

在C#中,要使用指针,您需要在编译时启用unsafe上下文,然后使用fixed关键字来标记指针变量。以下是一个简单的示例:

 1 unsafe void Example()
 2 {
 3     int x = 10;
 4     int* ptr = &x; //声明一个指向整数的指针
 5 
 6     //使用指针来修改变量的值
 7     *ptr = 20;
 8 
 9     Console.WriteLine(x); //输出 20
10 }

要使用指针,您需要在项目属性中启用"允许不安全代码"选项。使用指针时要非常小心,以避免内存泄漏和潜在的安全漏洞。 C# 引入了指针类型的概念,以帮助程序员更容易地进行指针操作,但仍然需要小心谨慎。如果不熟悉指针和内存管理,最好避免使用它们,除非绝对必要。

posted @ 2023-11-01 19:20  程序员胡大圣  阅读(1185)  评论(0)    收藏  举报