title: Why EXC_BAD_ACCESS (code=EXC_I386_GPFLT) when callback pointer to function?
body: <p>The following code is trying to count word frequency in a document, by using <code>hashset</code> and <code>vector</code>.</p>
  <pre class="lang-c prettyprint-override"><code>#include &lt;assert.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;string.h&gt;
  
  
  /** ==================================== VECTOR ======================================= */
  
  typedef enum {
      true, false
  } bool;
  
  typedef int (*VectorCompareFunction)(const void *elemAddr1, const void *elemAddr2);
  typedef void (*VectorFreeFunction)(void *elemAddr);
  
  typedef struct {
      int elemSize;               //how many byte for each element
      int elemNum;                //number of current element in vector
      int capacity;               //maximum number of element vector can hold
      void *elems;                //pointer to data memory
      VectorFreeFunction freefn;  //pointer to the function used to free each element
  } vector;
  
  /**
   * Reallocate a new memory of twice of original size
   * return 1 if reallocation success, otherwise return -1.
   */
  static void DoubleMemory(vector *v) {
      void *tmp = realloc(v-&gt;elems, v-&gt;capacity * v-&gt;elemSize * 2);
      assert(tmp != NULL);
      v-&gt;elems = tmp;
      v-&gt;capacity *= 2;
  }
  
  /**
   * Constructor
   */
  void VectorNew(vector *v, int elemSize, VectorFreeFunction freefn, int initialAllocation) {
      v-&gt;elems = malloc(initialAllocation * elemSize);
      assert(v-&gt;elems != NULL);
      v-&gt;elemSize = elemSize;
      v-&gt;elemNum = 0;
      v-&gt;capacity = initialAllocation;
      v-&gt;freefn = freefn;
  }
  
  /**
   * Appends a new element to the end of the specified vector.
   */
  void VectorAppend(vector *v, const void *elemAddr) {
      /* double size if neccessary */
      if (v-&gt;elemNum == v-&gt;capacity) DoubleMemory(v);
      memcpy((char *)v-&gt;elems + v-&gt;elemNum * v-&gt;elemSize, elemAddr, v-&gt;elemSize);
      v-&gt;elemNum++;
  }
  
  /**
   * Search the specified vector for an element whose contents match the element passed as the key.
   */
  int VectorSearch(const vector *v, const void *key, VectorCompareFunction searchfn, int startIndex, bool isSorted) {
      assert(key &amp;&amp; searchfn);
      if (v-&gt;elemNum == 0) return -1;
      assert(startIndex &gt;= 0 &amp;&amp; startIndex &lt; v-&gt;elemNum);
      if (isSorted == true) {
          /* binary search */
          void *startAddr = (char *)v-&gt;elems + startIndex * v-&gt;elemSize;
          int size = v-&gt;elemNum - startIndex;
          void *resAddr = bsearch(key, startAddr, size, v-&gt;elemSize, searchfn);
          return (resAddr != NULL)? ((char *)resAddr - (char *)v-&gt;elems) / v-&gt;elemSize : -1;
      } else {
          /* linear search */
          for (int i = 0; i &lt; v-&gt;elemNum; i++) {
              if (searchfn((char *)v-&gt;elems + i * v-&gt;elemSize, key) == 0) {
                  return i;
              }
          }
          return -1;
      }
  }
  
  /**
   * Overwrites the element at the specified position.
   */
  void VectorReplace(vector *v, const void *elemAddr, int position) {
      assert(position &gt;= 0 &amp;&amp; position &lt; v-&gt;elemNum);
      void *posAddr = (char *)v-&gt;elems + position * v-&gt;elemSize;
      /* free the memory of old element first */
      if (v-&gt;freefn != NULL) v-&gt;freefn(posAddr);
      memcpy(posAddr, elemAddr, v-&gt;elemSize);
  }
  
  
  
  
  
  /** ==================================== HASHSET ======================================= */
  
  typedef int (*HashSetHashFunction)(const void *elemAddr, int numBuckets);
  typedef int (*HashSetCompareFunction)(const void *elemAddr1, const void *elemAddr2);
  typedef void (*HashSetFreeFunction)(void *elemAddr);
  
  typedef struct {
      int elemNum;            //current element number
      int bucketNum;          //number of hash bucket
      int elemSize;           //how many byte each element has
      vector *buckets;        //array of vector
      HashSetHashFunction hashfn;
      HashSetCompareFunction compfn;
      HashSetFreeFunction freefn;
  } hashset;
  
  void HashSetNew(hashset *h, int elemSize, int numBuckets,   
          HashSetHashFunction hashfn, HashSetCompareFunction comparefn, HashSetFreeFunction freefn) {
      assert(elemSize &gt; 0 &amp;&amp; numBuckets &gt; 0 &amp;&amp; hashfn != NULL &amp;&amp; comparefn != NULL);
      h-&gt;buckets = (vector *)malloc(numBuckets * sizeof(vector));
      assert(h-&gt;buckets != NULL);
      for (int i = 0; i &lt; numBuckets; i++) {
          vector *bucket = (vector *)((char *)h-&gt;buckets + i * sizeof(vector));
          VectorNew(bucket, elemSize, freefn, 4);
      }
      h-&gt;bucketNum = numBuckets;
      h-&gt;elemSize = elemSize;
      h-&gt;elemNum = 0;
      h-&gt;hashfn = hashfn;
      h-&gt;compfn = comparefn;
      h-&gt;freefn = freefn;
  }
  
  void HashSetEnter(hashset *h, const void *elemAddr) {
      int hash = h-&gt;hashfn(elemAddr, h-&gt;bucketNum);
      vector *bucket = (vector *)((char *)h-&gt;buckets + hash * sizeof(vector));
      // search in the hash set first
      int pos = VectorSearch(bucket, elemAddr, h-&gt;compfn, 0, false);
      if (pos != -1) {
          // replace the old one if find a match
          VectorReplace(bucket, elemAddr, pos);
      } else {
          // otherwise insert the new one
          VectorAppend(bucket, elemAddr);
          h-&gt;elemNum++;
      }
  }
  
  
  
  
  /** ==================================== DOC_FREQ &amp; WORD_INDEX ======================================= */
  
  /****************************************************************
   *
   * doc_freq is a key-value pair of [documentid, frequency]
   * It's not supposed to be exposed to user or search engine.
   * -----------------------------------------------------------
   * It looks like:
   *      [1611742826915764000]   [4      ]  
   *      |-------------------|   |-------|
   *       docid                   freq
   ***************************************************************/
  typedef struct {
      long docid;
      int freq;
  } doc_freq;
  
  static void new_docfreq(doc_freq *df, long docid, int freq) {
      df-&gt;docid = docid;
      df-&gt;freq = freq;
  }
  
  /**
   * HashSetHashFunction&lt;doc_freq&gt;
   */
  static int hash_docfreq(const void *elemAddr, int numBuckets) {
      doc_freq *df = (doc_freq *)elemAddr;
      return (int)(df-&gt;docid % numBuckets);
  }
  
  /**
   * HashSetCompareFunction&lt;doc_freq&gt;
   */
  static int comp_docfreq(const void *elemAddr1, const void *elemAddr2) {
      long id1 = ((doc_freq *)elemAddr1)-&gt;docid;
      long id2 = ((doc_freq *)elemAddr2)-&gt;docid;
      if (id1 &lt; id2) {
          return -1;
      } else if (id1 &gt; id2) {
          return 1;
      } else { // id1 == id2
          return 0;
      }
  }
  
  /**
   * word_index is a index of a single word.
   * ---------------------------------------
   * A typical word_index looks like:
   *      [apple]: [doc1, 5], [doc3, 10], [doc5, 7]
   *      |-----|  |------------------------------|
   *       word     freqs
   */
  typedef struct {
      char *word;
      hashset *freqs; // hashset&lt;doc_freq&gt;
  } word_index;
  
  static const size_t kWordIndexHashSetBuckets = 64;
  static void new_wordindex(word_index *wi, const char *word) {
      hashset h;
      HashSetNew(&amp;h, sizeof(doc_freq), kWordIndexHashSetBuckets, hash_docfreq, comp_docfreq, NULL);
      wi-&gt;freqs = &amp;h;
      size_t wordlen = strlen(word);
      wi-&gt;word = (char *)malloc(wordlen + 1); // +1 for null-termination
      strcpy(wi-&gt;word, word);
      (wi-&gt;word)[wordlen] = '\0';
  }
  
  /**
   * Mainly used to build a word_index.
   */
  void add_docfreq(word_index *wi, const long docid, const int frequency) {
      doc_freq df;
      new_docfreq(&amp;df, docid, frequency);
      HashSetEnter(wi-&gt;freqs, &amp;df);
  }
  
  
  
  
  
  /** ==================================== UNIT-TEST ======================================= */
  
  int main(void) {
      /* apple:   [1611742826915764000, 5][1611742826915538000, 10] */
      word_index *apple = (word_index *)malloc(sizeof(word_index));
      new_wordindex(apple, &quot;apple&quot;);
      add_docfreq(apple, 1611742826915764000L, 5);
      add_docfreq(apple, 1611742826915538000L, 10);
  }
  </code></pre>
  <p>It gave me a <code>segmentation fault</code>:</p>
  <pre><code>[1]    84309 segmentation fault  testindexer
  </code></pre>
  <p>lldb find the problem occured when <code>hashset</code> try to callback the given pointer of function <code>hashfn</code>. I don't quite understand what is <code>EXC_BAD_ACCESS (code=EXC_I386_GPFLT)</code> here. I have done several unit test on hashset before, the <code>HashSetEnter()</code> function worked well with <code>hashfn</code>. Another unit test was conducted on <code>hash_docfreq()</code> function, it can also calculate correctly the hash number. I'm a little bit confused. Anyone can help? Thanks!</p>
  <pre><code>Process 89962 stopped
  * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
      frame #0: 0x0000000100003b83 testnothing`HashSetEnter(h=0x00007ffeefbff620, elemAddr=0x00007ffeefbff638) at test_nothing.c:130:13
     127  }
     128
     129  void HashSetEnter(hashset *h, const void *elemAddr) {
  -&gt; 130      int hash = h-&gt;hashfn(elemAddr, h-&gt;bucketNum);
     131      vector *bucket = (vector *)((char *)h-&gt;buckets + hash * sizeof(vector));
     132      // search in the hash set first
     133      int pos = VectorSearch(bucket, elemAddr, h-&gt;compfn, 0, false);
  Target 0: (testnothing) stopped.
  (lldb) bt
  * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    * frame #0: 0x0000000100003b83 testnothing`HashSetEnter(h=0x00007ffeefbff620, elemAddr=0x00007ffeefbff638) at test_nothing.c:130:13
      frame #1: 0x0000000100003c37 testnothing`add_docfreq(wi=0x0000000100306060, docid=1611742826915764000, frequency=5) at test_nothing.c:222:2
      frame #2: 0x0000000100003cae testnothing`main at test_nothing.c:235:2
      frame #3: 0x00007fff70df0cc9 libdyld.dylib`start + 1
  (lldb)
  </code></pre>