dongyin6576 2016-11-04 11:12
浏览 81
已采纳

无效的内存地址或使用sql.DB的nil指针取消引用

I'm learning Go at the moment and trying to make a little SQL-toolset:

type DBUtils struct {
  User string
  Password string
  Host string
  Database string
  Handle *sql.DB
}

func (dbUtil DBUtils) Connect() {
  var err error
  dbUtil.Handle, err = sql.Open("mysql", dbUtil.User + ":" + dbUtil.Password + "@tcp(" + dbUtil.Host + ")/" + dbUtil.Database)
  if err != nil {
    panic(err.Error()) 
  }

  err = dbUtil.Handle.Ping()
  if err != nil {
    panic(err.Error())
  }

  fmt.Printf("%v", dbUtil)
}

func (dbUtil DBUtils) Close() {
  dbUtil.Handle.Close()
}

func (dbUtil DBUtils) GetString(what string, from string, where string, wherevalue string) string {
  var username string

  fmt.Printf("%v", dbUtil)

  stmtOut, err := dbUtil.Handle.Prepare("SELECT " + what + " FROM " + from + " WHERE " + where + " = " + wherevalue)
  if err != nil {
      panic(err.Error()) // proper error handling instead of panic in your app
  }

  err = stmtOut.QueryRow(1).Scan(&username)

  return username
}

So when using this with the following code:

db := databaseutils.DBUtils{"root", "root", "127.0.0.1:3306", "gotest", nil}    
db.Connect() // I get: {root root 127.0.0.1:3306 gotest 0xc42019d600}
fmt.Printf("%v", db) // I get {root root 127.0.0.1:3306 gotest <nil>}
x := db.GetString("username", "users", "id", "1") // Doesn't work: panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(x)

For me it seems like my DB handle isn't saved properly? Does anyone have an idea - I'm pretty new to go and there are many things looking different to PHP, JS, C++ etc.

Thanks in advance!

  • 写回答

2条回答 默认 最新

  • donglu5235 2016-11-04 15:10
    关注

    Your Connect method is not changing the state of the object you're calling the method on. You're working on a copy of the type. If you want a method to change the object itself, you'll have to define it on a pointer:

    func (dbUtil *DBUtils) Connect() {
    //instead of
    func (dbUtil DBUtils) Connect() {
    

    If you're familiar with C or C++, your current method works similarly to something like:

    void connect_db(struct db_utils db_util)
    {}
    

    When you call a function like that, you're creating a copy of the argument, and push that on to the stack. The connect_db function will work with that, and after it returns, the copy is deallocated.

    compare it to this C code:

    struct foo {
        int bar;
    };
    static
    void change_copy(struct foo bar)
    {
        bar.bar *= 2;
    }
    
    static
    void change_ptr(struct foo *bar)
    {
        bar->bar *= 2;
    }
    
    int main ( void )
    {
        struct foo bar = {10};
        printf("%d
    ", bar.bar);//prints 10
        change_copy(bar);//pass by value
        printf("%d
    ", bar.bar);//still prints 10
        change_ptr(&bar);
        printf("%d
    ", bar.bar);//prints 20
        return 0;
    }
    

    The same thing happens in go. The object on which you define the method can only change state of the instance if it has access to the instance. If not, it can't update that part of the memory.

    In case you're wondering, this method doesn't need to be defined on the pointer type:

    func (dbUtil DBUtils) Close() {
      dbUtil.Handle.Close()
    }
    

    The reason for this being that DBUtils.Handle is a pointer type. A copy of that pointer will always point to the same resource.

    I will say this, though: given that you're essentially wrapping the handle, and you're exposing the Connect and Close methods, the member itself really shouldn't be exported. I'd change it to lower-case handle

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥100 求数学坐标画圆以及直线的算法
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable
  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 自己瞎改改,结果现在又运行不了了
  • ¥15 链式存储应该如何解决
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站