我正在进行一项任务,我们需要给出一个二维数组,我们需要进行一些计算。
我想将计算的逻辑封装到它自己的类中,所以在我的计算类中我有成员char **,我试图用给定的char arr [5] [5]初始化。我首先尝试做一个c风格的演员,让它编译,但如果我试图尊重我的成员变量会导致段错误。
简而言之,为什么这不起作用
class MyClass{
Myclass(char arr[5][5]){
m_PmyArr = arr; //doesn't work
}
char** m_PmyArr;
};
为什么这会编译,但会在取消引用后导致段错误
class MyClass{
Myclass(char arr[5][5]){
m_PmyArr = (char**)arr; //will compile, but will segfault
}
char** m_PmyArr;
};
我希望这不会被解释为"我的工作对我而言,我真的一直试图更好地掌握指针的细微差别,这与我之前理解的关系是相反的。数组和指针
答案 0 :(得分:5)
int[5][5]
与int**
不同。虽然数组在传递给函数时可以衰减为指针,但这并不会发生在多个数组级别。所以两者无法转换。
即使你使指针兼容,你也需要考虑指针的来源。如果它来自局部变量,并且该对象比它创建的函数更长,则指针将变为无效。
您应该动态分配内存:
class MyClass{
Myclass(int rows, int cols){
int i;
this->rows = rows;
this->cols = cols;
m_PmyArr = new int*[rows];
for (i=0; i<rows; i++) {
m_PmyArr[i] = new int[cols];
}
}
~Myclass() {
int i;
for (i=0; i<rows; i++) {
delete[] m_PmyArr[i];
}
delete[] m_PmyArr;
}
char** m_PmyArr;
int rows, cols;
};
答案 1 :(得分:4)
一旦你通过单个间接指针,我发现从概念上开始思考这些结构意味着什么是有帮助的。这是一张图片,说明char**
之间的差异,它指向char*
...和char[5][5]
的外观。
这两者不可互换。使用char[5][5]
时,您无法指向指向char
的指针...因为每个“元素”不是char*
,而是char[5]
。 ..你需要一个指向的指针。
答案 2 :(得分:3)
m_PmyArr
的类型为char**
,而在m_PmyArr = arr
语句中,arr
(在衰变后)的类型为char (*)[5]
。 m_PmyArr
和arr
都是不兼容的类型
将arr
强制转换为char**
只会关闭编译器警告,忽略程序的任何错误行为。
答案 3 :(得分:2)
数组和指针很难处理,这就是你通常只使用std::vector
,std::string
等的原因。
让我们来看看你的构造函数:
Myclass(char arr[5][5])
这并不意味着您的意思。数组不能通过值传递;据说数组“衰减”到指向其第一个元素的指针。这正是这里发生的事情。
你的构造函数等同于以下语法怪物:
Myclass(char(*arr)[5])
(您可以通过将其添加到您的班级来轻松验证这一点。您的编译器应该抱怨重新定义。)
怎么来的?考虑一下:二维数组实际上是一维数组,只是它的元素类型有点特殊。就像数组int arr[5]
由5个int
元素组成一样,数组char arr[5][5]
由5个char[5]
元素组成。根据这一观察,我们可以得出结论 char[5]
是一种类型。
换句话说,构造函数真正接收的是指向数组的第一个元素的指针,其中每个元素都是char[5]
。
m_PmyArr = arr; //doesn't work
是的,因为m_PmyArr
的元素属于char*
类型,而不属于char[5]
。编译器阻止您分配指向不兼容类型的指针。
为什么这会编译,但会在取消引用后导致段错误
[...]
m_PmyArr = (char**)arr; //will compile, but will segfault
因为C样式转换会禁用编译器的错误检查工具。
正式地,在您的真实代码中,您可能会遇到未定义的行为,这可能会导致崩溃,因为char*
可能需要的机器内存空间少于({1}} char[5]
,所以你的程序可能有一些内存问题。那里有很多“可能”,但这就是未定义行为的本质。
答案 4 :(得分:1)
@IBAction func sendEmailButtonTapped(sender: AnyObject) {
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
let query = PFQuery(className: "datos_contacto")
query.getObjectInBackgroundWithId("G5w8G3kVBG", block: {
(questionObject: PFObject?, error: NSError?) -> Void in
let direccion: AnyObject! = questionObject!.objectForKey("dato_contacto")
self.emailConsulta = direccion as! String
print("Email=",self.emailConsulta)
})
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self // Extremely important to set the --mailComposeDelegate-- property, NOT the --delegate-- property
print (emailConsulta)
mailComposerVC.setToRecipients([emailConsulta])
mailComposerVC.setSubject("Enviado desde Pedro Villarejo App Clientes (iOs)...")
mailComposerVC.setMessageBody("Escriba aqui su texto", isHTML: false)
return mailComposerVC
}
func showSendMailErrorAlert() {
let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail. Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
sendMailErrorAlert.show()
}
// MARK: MFMailComposeViewControllerDelegate
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
不正确,因为class MyClass{
Myclass(char arr[5][5]){
m_PmyArr = arr; //doesn't work
}
char** m_PmyArr;
};
衰减到arr
,而不是char (*)[5]
。
char**
将编译但稍后会导致问题,具体取决于您如何使用成员变量以及用于构造对象的内容,正如您已经注意到的那样。
您可以使用以下内容:
class MyClass{
Myclass(char arr[5][5]){
m_PmyArr = (char**)arr; //will compile, but will segfault
}
char** m_PmyArr;
};
答案 5 :(得分:1)
类型不兼容。二维数组实际上是一维数组的并置,类似于一维数组中单个元素的并置。
您应该考虑使用std::vector
。但是,如果您希望 - 或者 - 坚持通过指针模拟二维数组,您可能会尝试使用模板。
class C {
private:
size_t rows, cols;
int **matrix;
public:
template <size_t NR, size_t NC> C(const int (&m)[NR][NC]) :
rows(NR), cols(NC), matrix(new int *[NR])
{
for(size_t r=0; r<rows; ++r){
matrix[r]=new int[cols];
std::copy(m[r], m[r]+cols, matrix[r]);
}
}
~C(){
for(size_t r=0; r<rows; r++)
delete[] matrix[r];
delete[] matrix;
}
};
答案 6 :(得分:0)
char **只是一个指向char的指针。为了使它代表一个二维数组,它必须指向一个指针列表,而这些指针又指向行。其他答案显示了这一点你必须手动执行指针算法。
使用char [5] [5],编译器可以计算出所需的内存和指针算法,以便在给定的索引处取消引用。这就是为什么它是一个不同的类型。 char [] [5]可以用作函数参数类型。它告诉编译器一行中有5个字符,因此它知道一行中另一行结束的内存块的位置。
答案 7 :(得分:0)
如果在编译时已知数组大小,您可以使用模板函数来计算数组维度并处理数组中的数据:
#include <iostream>
template <int w, int h>
void f(int(&a)[w][h])
{
std::cout << "Width: " << w << " Height: " << h << std::endl;
}
int main()
{
int a1[2][3];
int a2[4][5];
f(a1);
f(a2);
}
当然,如果你想动态分配内存(例如询问用户的尺寸),这将无效。