libjson-c中正确释放所有json_object对象内存,避免内存泄漏与Valgrind非法释放报错的方法
Correctly Freeing All
json_object Instances in libjson-c to Avoid Memory Leaks and Invalid Frees Let's break down what's happening here and fix your memory management issues with libjson-c. First, a quick refresher: libjson-c uses reference counting to manage object memory, and misunderstanding how this works is almost certainly causing your Valgrind errors.
Why Your Current Code Is Causing Problems
- Weak References Don't Need Manual
put: When you fetch child objects withjson_object_object_get_exorjson_object_array_get_idx, you're getting a weak reference to the object (the reference count isn't incremented). Callingjson_object_puton these child objects directly would over-decrement their reference count, leading to invalid frees when the root object tries to clean them up later. - Dangling Pointers from Internal Strings:
json_object_get_stringreturns a pointer to the internal buffer of thejson_object. If you save this pointer (like you did withserverName/team1Name) and then free the rootparsed_json, those pointers become dangling—using them later causes undefined behavior, and trying to free them directly is a double-free error. - Uncleaned Resources in Loops: If you're running this code in a loop, you're not freeing the manually allocated strings (like the ones you cast to
char*) between iterations, leading to memory leaks.
Step-by-Step Fix
Here's how to adjust your code to handle memory correctly, with explanations of key changes:
// Declare persistent variables outside the loop so we can clean them up between iterations char* serverName = NULL; char* team1Name = NULL; char* team2Name = NULL; while (/* Your loop condition here */) { // Parse the config file (we own this object, so we'll need to put it later) struct json_object* parsed_json = json_object_from_file("config.json"); if (!parsed_json) { printf("Failed to load config.json\n"); goto cleanup_loop; // Clean up before exiting iteration } // Fetch and validate each field (all these are weak references) struct json_object* portInConfig = NULL; if (!json_object_object_get_ex(parsed_json, "port", &portInConfig)) { printf("Failed to find port number in config\n"); goto cleanup_json; } struct json_object* masterInConfig = NULL; if (!json_object_object_get_ex(parsed_json, "master", &masterInConfig)) { printf("Failed to find master variable in config\n"); goto cleanup_json; } // ... Repeat this pattern for all other config fields ... // Handle strings: copy to our own buffer (don't rely on internal json pointers) free(serverName); // Clean up previous iteration's allocation serverName = strdup(json_object_get_string(serverNameInConfig)); if (!serverName) { printf("Failed to allocate memory for server name\n"); goto cleanup_json; } free(team1Name); team1Name = strdup(json_object_get_string(team1NameInConfig)); if (!team1Name) { printf("Failed to allocate memory for team1 name\n"); goto cleanup_json; } free(team2Name); team2Name = strdup(json_object_get_string(team2NameInConfig)); if (!team2Name) { printf("Failed to allocate memory for team2 name\n"); goto cleanup_json; } // Handle array values (still weak references, no need to put) uint8 team1Color[3]; uint8 team2Color[3]; for (int i = 0; i < 3; ++i) { team1Color[i] = json_object_get_int(json_object_array_get_idx(team1ColorInConfig, i)); team2Color[i] = json_object_get_int(json_object_array_get_idx(team2ColorInConfig, i)); } uint8 mapArrayLen = json_object_array_length(mapInConfig); char mapArray[mapArrayLen][64]; // Stack-allocated, auto-cleaned when loop iteration ends for (int i = 0; i < mapArrayLen; ++i) { struct json_object* mapObj = json_object_array_get_idx(mapInConfig, i); const char* mapStr = json_object_get_string(mapObj); size_t strLen = strlen(mapStr); if (strLen >= 64) continue; strncpy(mapArray[i], mapStr, strLen); mapArray[i][strLen] = '\0'; // Ensure proper string termination } // Extract scalar values (safe, since we're copying the value, not a pointer) int port = json_object_get_int(portInConfig); int master = json_object_get_int(masterInConfig); uint8 gamemode = json_object_get_int(gamemodeInConfig); cleanup_json: // Free the root object: this recursively frees ALL child objects (portInConfig, etc.) json_object_put(parsed_json); // If we hit an error, skip to next iteration after cleaning up if (/* Check for error state here */) { continue; } // ... Your business logic using the parsed values goes here ... cleanup_loop: // If we exit the iteration early, ensure we don't leave dangling allocations // (Note: strdup returns NULL on failure, so free(NULL) is safe) free(serverName); serverName = NULL; free(team1Name); team1Name = NULL; free(team2Name); team2Name = NULL; } // Clean up persistent variables after the loop finishes free(serverName); free(team1Name); free(team2Name);
Key Takeaways
- Only
putWhat You Own: You only need to calljson_object_puton objects you created directly (like the one returned byjson_object_from_file). Child objects fetched viaget_exorarray_get_idxare managed by the root object. - Copy Internal Strings: Never save pointers returned by
json_object_get_stringlong-term—usestrdupto copy them to your own buffer, and free that buffer when you're done. - Clean Up Resources in Loops: Always free manually allocated memory (like the duplicated strings) between loop iterations to avoid leaks.
- Validate Types (Optional but Recommended): Add checks with
json_object_is_type(e.g.,json_object_is_type(portInConfig, json_type_int)) to ensure the config file has the correct data types, preventing crashes from invalid casts.
内容的提问来源于stack exchange,提问作者Haxk20
相关产品推荐
相关产品推荐

